@ai-sdk/anthropic 3.0.18 → 3.0.19
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 +6 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -2
- package/src/__fixtures__/anthropic-code-execution-20250825.1.chunks.txt +248 -0
- package/src/__fixtures__/anthropic-code-execution-20250825.1.json +70 -0
- package/src/__fixtures__/anthropic-code-execution-20250825.2.chunks.txt +984 -0
- package/src/__fixtures__/anthropic-code-execution-20250825.2.json +111 -0
- package/src/__fixtures__/anthropic-code-execution-20250825.pptx-skill.chunks.txt +691 -0
- package/src/__fixtures__/anthropic-code-execution-20250825.pptx-skill.json +1801 -0
- package/src/__fixtures__/anthropic-json-other-tool.1.chunks.txt +13 -0
- package/src/__fixtures__/anthropic-json-other-tool.1.json +26 -0
- package/src/__fixtures__/anthropic-json-output-format.1.chunks.txt +120 -0
- package/src/__fixtures__/anthropic-json-output-format.1.json +25 -0
- package/src/__fixtures__/anthropic-json-tool.1.chunks.txt +9 -0
- package/src/__fixtures__/anthropic-json-tool.1.json +37 -0
- package/src/__fixtures__/anthropic-json-tool.2.chunks.txt +14 -0
- package/src/__fixtures__/anthropic-mcp.1.chunks.txt +17 -0
- package/src/__fixtures__/anthropic-mcp.1.json +39 -0
- package/src/__fixtures__/anthropic-memory-20250818.1.json +28 -0
- package/src/__fixtures__/anthropic-message-delta-input-tokens.chunks.txt +8 -0
- package/src/__fixtures__/anthropic-programmatic-tool-calling.1.chunks.txt +278 -0
- package/src/__fixtures__/anthropic-programmatic-tool-calling.1.json +106 -0
- package/src/__fixtures__/anthropic-tool-no-args.chunks.txt +13 -0
- package/src/__fixtures__/anthropic-tool-no-args.json +31 -0
- package/src/__fixtures__/anthropic-tool-search-bm25.1.chunks.txt +47 -0
- package/src/__fixtures__/anthropic-tool-search-bm25.1.json +67 -0
- package/src/__fixtures__/anthropic-tool-search-regex.1.chunks.txt +51 -0
- package/src/__fixtures__/anthropic-tool-search-regex.1.json +65 -0
- package/src/__fixtures__/anthropic-web-fetch-tool.1.chunks.txt +64 -0
- package/src/__fixtures__/anthropic-web-fetch-tool.1.json +54 -0
- package/src/__fixtures__/anthropic-web-fetch-tool.2.json +56 -0
- package/src/__fixtures__/anthropic-web-fetch-tool.error.json +46 -0
- package/src/__fixtures__/anthropic-web-search-tool.1.chunks.txt +120 -0
- package/src/__fixtures__/anthropic-web-search-tool.1.json +181 -0
- package/src/__snapshots__/anthropic-messages-language-model.test.ts.snap +16719 -0
- package/src/anthropic-error.test.ts +42 -0
- package/src/anthropic-error.ts +26 -0
- package/src/anthropic-message-metadata.ts +105 -0
- package/src/anthropic-messages-api.ts +1188 -0
- package/src/anthropic-messages-language-model.test.ts +7170 -0
- package/src/anthropic-messages-language-model.ts +2067 -0
- package/src/anthropic-messages-options.ts +213 -0
- package/src/anthropic-prepare-tools.test.ts +1219 -0
- package/src/anthropic-prepare-tools.ts +341 -0
- package/src/anthropic-provider.test.ts +162 -0
- package/src/anthropic-provider.ts +152 -0
- package/src/anthropic-tools.ts +182 -0
- package/src/convert-anthropic-messages-usage.ts +32 -0
- package/src/convert-to-anthropic-messages-prompt.test.ts +2902 -0
- package/src/convert-to-anthropic-messages-prompt.ts +1050 -0
- package/src/forward-anthropic-container-id-from-last-step.ts +38 -0
- package/src/get-cache-control.ts +63 -0
- package/src/index.ts +10 -0
- package/src/internal/index.ts +4 -0
- package/src/map-anthropic-stop-reason.ts +28 -0
- package/src/tool/bash_20241022.ts +33 -0
- package/src/tool/bash_20250124.ts +33 -0
- package/src/tool/code-execution_20250522.ts +61 -0
- package/src/tool/code-execution_20250825.ts +281 -0
- package/src/tool/computer_20241022.ts +87 -0
- package/src/tool/computer_20250124.ts +130 -0
- package/src/tool/memory_20250818.ts +62 -0
- package/src/tool/text-editor_20241022.ts +63 -0
- package/src/tool/text-editor_20250124.ts +63 -0
- package/src/tool/text-editor_20250429.ts +64 -0
- package/src/tool/text-editor_20250728.ts +80 -0
- package/src/tool/tool-search-bm25_20251119.ts +98 -0
- package/src/tool/tool-search-regex_20251119.ts +110 -0
- package/src/tool/web-fetch-20250910.ts +145 -0
- package/src/tool/web-search_20250305.ts +136 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LanguageModelV3CallOptions,
|
|
3
|
+
SharedV3Warning,
|
|
4
|
+
UnsupportedFunctionalityError,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import { AnthropicTool, AnthropicToolChoice } from './anthropic-messages-api';
|
|
7
|
+
import { CacheControlValidator } from './get-cache-control';
|
|
8
|
+
import { textEditor_20250728ArgsSchema } from './tool/text-editor_20250728';
|
|
9
|
+
import { webSearch_20250305ArgsSchema } from './tool/web-search_20250305';
|
|
10
|
+
import { webFetch_20250910ArgsSchema } from './tool/web-fetch-20250910';
|
|
11
|
+
import { validateTypes } from '@ai-sdk/provider-utils';
|
|
12
|
+
|
|
13
|
+
export interface AnthropicToolOptions {
|
|
14
|
+
deferLoading?: boolean;
|
|
15
|
+
allowedCallers?: Array<'code_execution_20250825'>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function prepareTools({
|
|
19
|
+
tools,
|
|
20
|
+
toolChoice,
|
|
21
|
+
disableParallelToolUse,
|
|
22
|
+
cacheControlValidator,
|
|
23
|
+
supportsStructuredOutput,
|
|
24
|
+
}: {
|
|
25
|
+
tools: LanguageModelV3CallOptions['tools'];
|
|
26
|
+
toolChoice: LanguageModelV3CallOptions['toolChoice'] | undefined;
|
|
27
|
+
disableParallelToolUse?: boolean;
|
|
28
|
+
cacheControlValidator?: CacheControlValidator;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Whether the model supports structured output.
|
|
32
|
+
*/
|
|
33
|
+
supportsStructuredOutput: boolean;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
tools: Array<AnthropicTool> | undefined;
|
|
36
|
+
toolChoice: AnthropicToolChoice | undefined;
|
|
37
|
+
toolWarnings: SharedV3Warning[];
|
|
38
|
+
betas: Set<string>;
|
|
39
|
+
}> {
|
|
40
|
+
// when the tools array is empty, change it to undefined to prevent errors:
|
|
41
|
+
tools = tools?.length ? tools : undefined;
|
|
42
|
+
|
|
43
|
+
const toolWarnings: SharedV3Warning[] = [];
|
|
44
|
+
const betas = new Set<string>();
|
|
45
|
+
const validator = cacheControlValidator || new CacheControlValidator();
|
|
46
|
+
|
|
47
|
+
if (tools == null) {
|
|
48
|
+
return { tools: undefined, toolChoice: undefined, toolWarnings, betas };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const anthropicTools: AnthropicTool[] = [];
|
|
52
|
+
|
|
53
|
+
for (const tool of tools) {
|
|
54
|
+
switch (tool.type) {
|
|
55
|
+
case 'function': {
|
|
56
|
+
const cacheControl = validator.getCacheControl(tool.providerOptions, {
|
|
57
|
+
type: 'tool definition',
|
|
58
|
+
canCache: true,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Read Anthropic-specific provider options
|
|
62
|
+
const anthropicOptions = tool.providerOptions?.anthropic as
|
|
63
|
+
| AnthropicToolOptions
|
|
64
|
+
| undefined;
|
|
65
|
+
const deferLoading = anthropicOptions?.deferLoading;
|
|
66
|
+
const allowedCallers = anthropicOptions?.allowedCallers;
|
|
67
|
+
|
|
68
|
+
anthropicTools.push({
|
|
69
|
+
name: tool.name,
|
|
70
|
+
description: tool.description,
|
|
71
|
+
input_schema: tool.inputSchema,
|
|
72
|
+
cache_control: cacheControl,
|
|
73
|
+
...(supportsStructuredOutput === true && tool.strict != null
|
|
74
|
+
? { strict: tool.strict }
|
|
75
|
+
: {}),
|
|
76
|
+
...(deferLoading != null ? { defer_loading: deferLoading } : {}),
|
|
77
|
+
...(allowedCallers != null
|
|
78
|
+
? { allowed_callers: allowedCallers }
|
|
79
|
+
: {}),
|
|
80
|
+
...(tool.inputExamples != null
|
|
81
|
+
? {
|
|
82
|
+
input_examples: tool.inputExamples.map(
|
|
83
|
+
example => example.input,
|
|
84
|
+
),
|
|
85
|
+
}
|
|
86
|
+
: {}),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (supportsStructuredOutput === true) {
|
|
90
|
+
betas.add('structured-outputs-2025-11-13');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (tool.inputExamples != null || allowedCallers != null) {
|
|
94
|
+
betas.add('advanced-tool-use-2025-11-20');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case 'provider': {
|
|
101
|
+
// Note: Provider-defined tools don't currently support providerOptions in the SDK,
|
|
102
|
+
// so cache_control cannot be set on them. The Anthropic API supports caching all tools,
|
|
103
|
+
// but the SDK would need to be updated to expose providerOptions on provider-defined tools.
|
|
104
|
+
switch (tool.id) {
|
|
105
|
+
case 'anthropic.code_execution_20250522': {
|
|
106
|
+
betas.add('code-execution-2025-05-22');
|
|
107
|
+
anthropicTools.push({
|
|
108
|
+
type: 'code_execution_20250522',
|
|
109
|
+
name: 'code_execution',
|
|
110
|
+
cache_control: undefined,
|
|
111
|
+
});
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case 'anthropic.code_execution_20250825': {
|
|
115
|
+
betas.add('code-execution-2025-08-25');
|
|
116
|
+
anthropicTools.push({
|
|
117
|
+
type: 'code_execution_20250825',
|
|
118
|
+
name: 'code_execution',
|
|
119
|
+
});
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'anthropic.computer_20250124': {
|
|
123
|
+
betas.add('computer-use-2025-01-24');
|
|
124
|
+
anthropicTools.push({
|
|
125
|
+
name: 'computer',
|
|
126
|
+
type: 'computer_20250124',
|
|
127
|
+
display_width_px: tool.args.displayWidthPx as number,
|
|
128
|
+
display_height_px: tool.args.displayHeightPx as number,
|
|
129
|
+
display_number: tool.args.displayNumber as number,
|
|
130
|
+
cache_control: undefined,
|
|
131
|
+
});
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case 'anthropic.computer_20241022': {
|
|
135
|
+
betas.add('computer-use-2024-10-22');
|
|
136
|
+
anthropicTools.push({
|
|
137
|
+
name: 'computer',
|
|
138
|
+
type: 'computer_20241022',
|
|
139
|
+
display_width_px: tool.args.displayWidthPx as number,
|
|
140
|
+
display_height_px: tool.args.displayHeightPx as number,
|
|
141
|
+
display_number: tool.args.displayNumber as number,
|
|
142
|
+
cache_control: undefined,
|
|
143
|
+
});
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case 'anthropic.text_editor_20250124': {
|
|
147
|
+
betas.add('computer-use-2025-01-24');
|
|
148
|
+
anthropicTools.push({
|
|
149
|
+
name: 'str_replace_editor',
|
|
150
|
+
type: 'text_editor_20250124',
|
|
151
|
+
cache_control: undefined,
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case 'anthropic.text_editor_20241022': {
|
|
156
|
+
betas.add('computer-use-2024-10-22');
|
|
157
|
+
anthropicTools.push({
|
|
158
|
+
name: 'str_replace_editor',
|
|
159
|
+
type: 'text_editor_20241022',
|
|
160
|
+
cache_control: undefined,
|
|
161
|
+
});
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'anthropic.text_editor_20250429': {
|
|
165
|
+
betas.add('computer-use-2025-01-24');
|
|
166
|
+
anthropicTools.push({
|
|
167
|
+
name: 'str_replace_based_edit_tool',
|
|
168
|
+
type: 'text_editor_20250429',
|
|
169
|
+
cache_control: undefined,
|
|
170
|
+
});
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 'anthropic.text_editor_20250728': {
|
|
174
|
+
const args = await validateTypes({
|
|
175
|
+
value: tool.args,
|
|
176
|
+
schema: textEditor_20250728ArgsSchema,
|
|
177
|
+
});
|
|
178
|
+
anthropicTools.push({
|
|
179
|
+
name: 'str_replace_based_edit_tool',
|
|
180
|
+
type: 'text_editor_20250728',
|
|
181
|
+
max_characters: args.maxCharacters,
|
|
182
|
+
cache_control: undefined,
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case 'anthropic.bash_20250124': {
|
|
187
|
+
betas.add('computer-use-2025-01-24');
|
|
188
|
+
anthropicTools.push({
|
|
189
|
+
name: 'bash',
|
|
190
|
+
type: 'bash_20250124',
|
|
191
|
+
cache_control: undefined,
|
|
192
|
+
});
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case 'anthropic.bash_20241022': {
|
|
196
|
+
betas.add('computer-use-2024-10-22');
|
|
197
|
+
anthropicTools.push({
|
|
198
|
+
name: 'bash',
|
|
199
|
+
type: 'bash_20241022',
|
|
200
|
+
cache_control: undefined,
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case 'anthropic.memory_20250818': {
|
|
205
|
+
betas.add('context-management-2025-06-27');
|
|
206
|
+
anthropicTools.push({
|
|
207
|
+
name: 'memory',
|
|
208
|
+
type: 'memory_20250818',
|
|
209
|
+
});
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case 'anthropic.web_fetch_20250910': {
|
|
213
|
+
betas.add('web-fetch-2025-09-10');
|
|
214
|
+
const args = await validateTypes({
|
|
215
|
+
value: tool.args,
|
|
216
|
+
schema: webFetch_20250910ArgsSchema,
|
|
217
|
+
});
|
|
218
|
+
anthropicTools.push({
|
|
219
|
+
type: 'web_fetch_20250910',
|
|
220
|
+
name: 'web_fetch',
|
|
221
|
+
max_uses: args.maxUses,
|
|
222
|
+
allowed_domains: args.allowedDomains,
|
|
223
|
+
blocked_domains: args.blockedDomains,
|
|
224
|
+
citations: args.citations,
|
|
225
|
+
max_content_tokens: args.maxContentTokens,
|
|
226
|
+
cache_control: undefined,
|
|
227
|
+
});
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case 'anthropic.web_search_20250305': {
|
|
231
|
+
const args = await validateTypes({
|
|
232
|
+
value: tool.args,
|
|
233
|
+
schema: webSearch_20250305ArgsSchema,
|
|
234
|
+
});
|
|
235
|
+
anthropicTools.push({
|
|
236
|
+
type: 'web_search_20250305',
|
|
237
|
+
name: 'web_search',
|
|
238
|
+
max_uses: args.maxUses,
|
|
239
|
+
allowed_domains: args.allowedDomains,
|
|
240
|
+
blocked_domains: args.blockedDomains,
|
|
241
|
+
user_location: args.userLocation,
|
|
242
|
+
cache_control: undefined,
|
|
243
|
+
});
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
case 'anthropic.tool_search_regex_20251119': {
|
|
248
|
+
betas.add('advanced-tool-use-2025-11-20');
|
|
249
|
+
anthropicTools.push({
|
|
250
|
+
type: 'tool_search_tool_regex_20251119',
|
|
251
|
+
name: 'tool_search_tool_regex',
|
|
252
|
+
});
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
case 'anthropic.tool_search_bm25_20251119': {
|
|
257
|
+
betas.add('advanced-tool-use-2025-11-20');
|
|
258
|
+
anthropicTools.push({
|
|
259
|
+
type: 'tool_search_tool_bm25_20251119',
|
|
260
|
+
name: 'tool_search_tool_bm25',
|
|
261
|
+
});
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
default: {
|
|
266
|
+
toolWarnings.push({
|
|
267
|
+
type: 'unsupported',
|
|
268
|
+
feature: `provider-defined tool ${tool.id}`,
|
|
269
|
+
});
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
default: {
|
|
277
|
+
toolWarnings.push({
|
|
278
|
+
type: 'unsupported',
|
|
279
|
+
feature: `tool ${tool}`,
|
|
280
|
+
});
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (toolChoice == null) {
|
|
287
|
+
return {
|
|
288
|
+
tools: anthropicTools,
|
|
289
|
+
toolChoice: disableParallelToolUse
|
|
290
|
+
? { type: 'auto', disable_parallel_tool_use: disableParallelToolUse }
|
|
291
|
+
: undefined,
|
|
292
|
+
toolWarnings,
|
|
293
|
+
betas,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const type = toolChoice.type;
|
|
298
|
+
|
|
299
|
+
switch (type) {
|
|
300
|
+
case 'auto':
|
|
301
|
+
return {
|
|
302
|
+
tools: anthropicTools,
|
|
303
|
+
toolChoice: {
|
|
304
|
+
type: 'auto',
|
|
305
|
+
disable_parallel_tool_use: disableParallelToolUse,
|
|
306
|
+
},
|
|
307
|
+
toolWarnings,
|
|
308
|
+
betas,
|
|
309
|
+
};
|
|
310
|
+
case 'required':
|
|
311
|
+
return {
|
|
312
|
+
tools: anthropicTools,
|
|
313
|
+
toolChoice: {
|
|
314
|
+
type: 'any',
|
|
315
|
+
disable_parallel_tool_use: disableParallelToolUse,
|
|
316
|
+
},
|
|
317
|
+
toolWarnings,
|
|
318
|
+
betas,
|
|
319
|
+
};
|
|
320
|
+
case 'none':
|
|
321
|
+
// Anthropic does not support 'none' tool choice, so we remove the tools:
|
|
322
|
+
return { tools: undefined, toolChoice: undefined, toolWarnings, betas };
|
|
323
|
+
case 'tool':
|
|
324
|
+
return {
|
|
325
|
+
tools: anthropicTools,
|
|
326
|
+
toolChoice: {
|
|
327
|
+
type: 'tool',
|
|
328
|
+
name: toolChoice.toolName,
|
|
329
|
+
disable_parallel_tool_use: disableParallelToolUse,
|
|
330
|
+
},
|
|
331
|
+
toolWarnings,
|
|
332
|
+
betas,
|
|
333
|
+
};
|
|
334
|
+
default: {
|
|
335
|
+
const _exhaustiveCheck: never = type;
|
|
336
|
+
throw new UnsupportedFunctionalityError({
|
|
337
|
+
functionality: `tool choice type: ${_exhaustiveCheck}`,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { LanguageModelV3Prompt } from '@ai-sdk/provider';
|
|
4
|
+
import { createAnthropic } from './anthropic-provider';
|
|
5
|
+
|
|
6
|
+
vi.mock('./version', () => ({
|
|
7
|
+
VERSION: '0.0.0-test',
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
const TEST_PROMPT: LanguageModelV3Prompt = [
|
|
11
|
+
{ role: 'user', content: [{ type: 'text', text: 'Hello' }] },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const createSuccessfulResponse = () =>
|
|
15
|
+
new Response(
|
|
16
|
+
JSON.stringify({
|
|
17
|
+
type: 'message',
|
|
18
|
+
id: 'msg_123',
|
|
19
|
+
model: 'claude-3-haiku-20240307',
|
|
20
|
+
content: [{ type: 'text', text: 'Hi' }],
|
|
21
|
+
stop_reason: null,
|
|
22
|
+
stop_sequence: null,
|
|
23
|
+
usage: { input_tokens: 1, output_tokens: 1 },
|
|
24
|
+
}),
|
|
25
|
+
{
|
|
26
|
+
status: 200,
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const createFetchMock = () =>
|
|
32
|
+
vi.fn().mockResolvedValue(createSuccessfulResponse());
|
|
33
|
+
|
|
34
|
+
describe('createAnthropic', () => {
|
|
35
|
+
describe('baseURL configuration', () => {
|
|
36
|
+
const originalBaseUrl = process.env.ANTHROPIC_BASE_URL;
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
vi.restoreAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
if (originalBaseUrl === undefined) {
|
|
44
|
+
delete process.env.ANTHROPIC_BASE_URL;
|
|
45
|
+
} else {
|
|
46
|
+
process.env.ANTHROPIC_BASE_URL = originalBaseUrl;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('uses the default Anthropic base URL when not provided', async () => {
|
|
51
|
+
delete process.env.ANTHROPIC_BASE_URL;
|
|
52
|
+
|
|
53
|
+
const fetchMock = createFetchMock();
|
|
54
|
+
const provider = createAnthropic({
|
|
55
|
+
apiKey: 'test-api-key',
|
|
56
|
+
fetch: fetchMock,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
await provider('claude-3-haiku-20240307').doGenerate({
|
|
60
|
+
prompt: TEST_PROMPT,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
64
|
+
const [requestUrl] = fetchMock.mock.calls[0]!;
|
|
65
|
+
expect(requestUrl).toBe('https://api.anthropic.com/v1/messages');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('uses ANTHROPIC_BASE_URL when set', async () => {
|
|
69
|
+
process.env.ANTHROPIC_BASE_URL = 'https://proxy.anthropic.example/v1/';
|
|
70
|
+
|
|
71
|
+
const fetchMock = createFetchMock();
|
|
72
|
+
const provider = createAnthropic({
|
|
73
|
+
apiKey: 'test-api-key',
|
|
74
|
+
fetch: fetchMock,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await provider('claude-3-haiku-20240307').doGenerate({
|
|
78
|
+
prompt: TEST_PROMPT,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
82
|
+
const [requestUrl] = fetchMock.mock.calls[0]!;
|
|
83
|
+
expect(requestUrl).toBe('https://proxy.anthropic.example/v1/messages');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('prefers the baseURL option over ANTHROPIC_BASE_URL', async () => {
|
|
87
|
+
process.env.ANTHROPIC_BASE_URL = 'https://env.anthropic.example/v1';
|
|
88
|
+
|
|
89
|
+
const fetchMock = createFetchMock();
|
|
90
|
+
const provider = createAnthropic({
|
|
91
|
+
apiKey: 'test-api-key',
|
|
92
|
+
baseURL: 'https://option.anthropic.example/v1/',
|
|
93
|
+
fetch: fetchMock,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await provider('claude-3-haiku-20240307').doGenerate({
|
|
97
|
+
prompt: TEST_PROMPT,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
101
|
+
const [requestUrl] = fetchMock.mock.calls[0]!;
|
|
102
|
+
expect(requestUrl).toBe('https://option.anthropic.example/v1/messages');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('anthropic provider - custom provider name', () => {
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
vi.clearAllMocks();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should use custom provider name when specified', () => {
|
|
113
|
+
const provider = createAnthropic({
|
|
114
|
+
name: 'my-claude-proxy',
|
|
115
|
+
apiKey: 'test-api-key',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const model = provider('claude-3-haiku-20240307');
|
|
119
|
+
expect(model.provider).toBe('my-claude-proxy');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should default to anthropic.messages when name not specified', () => {
|
|
123
|
+
const provider = createAnthropic({
|
|
124
|
+
apiKey: 'test-api-key',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const model = provider('claude-3-haiku-20240307');
|
|
128
|
+
expect(model.provider).toBe('anthropic.messages');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('anthropic provider - supportedUrls', () => {
|
|
133
|
+
it('should support image/* URLs', async () => {
|
|
134
|
+
const provider = createAnthropic({
|
|
135
|
+
apiKey: 'test-api-key',
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const model = provider('claude-3-haiku-20240307');
|
|
139
|
+
const supportedUrls = await model.supportedUrls;
|
|
140
|
+
|
|
141
|
+
expect(supportedUrls['image/*']).toBeDefined();
|
|
142
|
+
expect(
|
|
143
|
+
supportedUrls['image/*']![0]!.test('https://example.com/image.png'),
|
|
144
|
+
).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should support application/pdf URLs', async () => {
|
|
148
|
+
const provider = createAnthropic({
|
|
149
|
+
apiKey: 'test-api-key',
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const model = provider('claude-3-haiku-20240307');
|
|
153
|
+
const supportedUrls = await model.supportedUrls;
|
|
154
|
+
|
|
155
|
+
expect(supportedUrls['application/pdf']).toBeDefined();
|
|
156
|
+
expect(
|
|
157
|
+
supportedUrls['application/pdf']![0]!.test(
|
|
158
|
+
'https://arxiv.org/pdf/2401.00001',
|
|
159
|
+
),
|
|
160
|
+
).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LanguageModelV3,
|
|
3
|
+
NoSuchModelError,
|
|
4
|
+
ProviderV3,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import {
|
|
7
|
+
FetchFunction,
|
|
8
|
+
generateId,
|
|
9
|
+
loadApiKey,
|
|
10
|
+
loadOptionalSetting,
|
|
11
|
+
withoutTrailingSlash,
|
|
12
|
+
withUserAgentSuffix,
|
|
13
|
+
} from '@ai-sdk/provider-utils';
|
|
14
|
+
import { VERSION } from './version';
|
|
15
|
+
import { AnthropicMessagesLanguageModel } from './anthropic-messages-language-model';
|
|
16
|
+
import { AnthropicMessagesModelId } from './anthropic-messages-options';
|
|
17
|
+
import { anthropicTools } from './anthropic-tools';
|
|
18
|
+
|
|
19
|
+
export interface AnthropicProvider extends ProviderV3 {
|
|
20
|
+
/**
|
|
21
|
+
Creates a model for text generation.
|
|
22
|
+
*/
|
|
23
|
+
(modelId: AnthropicMessagesModelId): LanguageModelV3;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
Creates a model for text generation.
|
|
27
|
+
*/
|
|
28
|
+
languageModel(modelId: AnthropicMessagesModelId): LanguageModelV3;
|
|
29
|
+
|
|
30
|
+
chat(modelId: AnthropicMessagesModelId): LanguageModelV3;
|
|
31
|
+
|
|
32
|
+
messages(modelId: AnthropicMessagesModelId): LanguageModelV3;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated Use `embeddingModel` instead.
|
|
36
|
+
*/
|
|
37
|
+
textEmbeddingModel(modelId: string): never;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
Anthropic-specific computer use tool.
|
|
41
|
+
*/
|
|
42
|
+
tools: typeof anthropicTools;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface AnthropicProviderSettings {
|
|
46
|
+
/**
|
|
47
|
+
Use a different URL prefix for API calls, e.g. to use proxy servers.
|
|
48
|
+
The default prefix is `https://api.anthropic.com/v1`.
|
|
49
|
+
*/
|
|
50
|
+
baseURL?: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
API key that is being send using the `x-api-key` header.
|
|
54
|
+
It defaults to the `ANTHROPIC_API_KEY` environment variable.
|
|
55
|
+
*/
|
|
56
|
+
apiKey?: string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
Custom headers to include in the requests.
|
|
60
|
+
*/
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
Custom fetch implementation. You can use it as a middleware to intercept requests,
|
|
65
|
+
or to provide a custom fetch implementation for e.g. testing.
|
|
66
|
+
*/
|
|
67
|
+
fetch?: FetchFunction;
|
|
68
|
+
|
|
69
|
+
generateId?: () => string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Custom provider name
|
|
73
|
+
* Defaults to 'anthropic.messages'.
|
|
74
|
+
*/
|
|
75
|
+
name?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
Create an Anthropic provider instance.
|
|
80
|
+
*/
|
|
81
|
+
export function createAnthropic(
|
|
82
|
+
options: AnthropicProviderSettings = {},
|
|
83
|
+
): AnthropicProvider {
|
|
84
|
+
const baseURL =
|
|
85
|
+
withoutTrailingSlash(
|
|
86
|
+
loadOptionalSetting({
|
|
87
|
+
settingValue: options.baseURL,
|
|
88
|
+
environmentVariableName: 'ANTHROPIC_BASE_URL',
|
|
89
|
+
}),
|
|
90
|
+
) ?? 'https://api.anthropic.com/v1';
|
|
91
|
+
|
|
92
|
+
const providerName = options.name ?? 'anthropic.messages';
|
|
93
|
+
|
|
94
|
+
const getHeaders = () =>
|
|
95
|
+
withUserAgentSuffix(
|
|
96
|
+
{
|
|
97
|
+
'anthropic-version': '2023-06-01',
|
|
98
|
+
'x-api-key': loadApiKey({
|
|
99
|
+
apiKey: options.apiKey,
|
|
100
|
+
environmentVariableName: 'ANTHROPIC_API_KEY',
|
|
101
|
+
description: 'Anthropic',
|
|
102
|
+
}),
|
|
103
|
+
...options.headers,
|
|
104
|
+
},
|
|
105
|
+
`ai-sdk/anthropic/${VERSION}`,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const createChatModel = (modelId: AnthropicMessagesModelId) =>
|
|
109
|
+
new AnthropicMessagesLanguageModel(modelId, {
|
|
110
|
+
provider: providerName,
|
|
111
|
+
baseURL,
|
|
112
|
+
headers: getHeaders,
|
|
113
|
+
fetch: options.fetch,
|
|
114
|
+
generateId: options.generateId ?? generateId,
|
|
115
|
+
supportedUrls: () => ({
|
|
116
|
+
'image/*': [/^https?:\/\/.*$/],
|
|
117
|
+
'application/pdf': [/^https?:\/\/.*$/],
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const provider = function (modelId: AnthropicMessagesModelId) {
|
|
122
|
+
if (new.target) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'The Anthropic model function cannot be called with the new keyword.',
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return createChatModel(modelId);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
provider.specificationVersion = 'v3' as const;
|
|
132
|
+
provider.languageModel = createChatModel;
|
|
133
|
+
provider.chat = createChatModel;
|
|
134
|
+
provider.messages = createChatModel;
|
|
135
|
+
|
|
136
|
+
provider.embeddingModel = (modelId: string) => {
|
|
137
|
+
throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });
|
|
138
|
+
};
|
|
139
|
+
provider.textEmbeddingModel = provider.embeddingModel;
|
|
140
|
+
provider.imageModel = (modelId: string) => {
|
|
141
|
+
throw new NoSuchModelError({ modelId, modelType: 'imageModel' });
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
provider.tools = anthropicTools;
|
|
145
|
+
|
|
146
|
+
return provider;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
Default Anthropic provider instance.
|
|
151
|
+
*/
|
|
152
|
+
export const anthropic = createAnthropic();
|