@ai-sdk/anthropic 4.0.41 → 4.0.44
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 +28 -0
- package/dist/index.js +90 -85
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +61 -10
- package/dist/internal/index.js.map +1 -1
- package/docs/05-anthropic.mdx +17 -8
- package/package.json +6 -6
- package/src/anthropic-api.ts +4 -0
- package/src/anthropic-language-model.ts +61 -5
- package/src/anthropic-messages-batch.ts +36 -89
- package/src/skills/anthropic-skills.ts +12 -1
package/docs/05-anthropic.mdx
CHANGED
|
@@ -155,12 +155,6 @@ Anthropic language models support asynchronous text generation through the
|
|
|
155
155
|
Use the experimental text batch APIs to start a batch, poll its status, and
|
|
156
156
|
stream its results:
|
|
157
157
|
|
|
158
|
-
<Note>
|
|
159
|
-
Anthropic Message Batches do not support the `speed` option. Explicit
|
|
160
|
-
`anthropicBeta` values must be configured when starting the batch rather than
|
|
161
|
-
on an individual request.
|
|
162
|
-
</Note>
|
|
163
|
-
|
|
164
158
|
```ts
|
|
165
159
|
import { anthropic } from '@ai-sdk/anthropic';
|
|
166
160
|
import {
|
|
@@ -175,8 +169,8 @@ const model = anthropic('claude-haiku-4-5');
|
|
|
175
169
|
const batch = await startTextBatch({
|
|
176
170
|
model,
|
|
177
171
|
requests: [
|
|
178
|
-
{ id: '
|
|
179
|
-
{ id: '
|
|
172
|
+
{ id: 'capital-france', prompt: 'What is the capital of France?' },
|
|
173
|
+
{ id: 'capital-germany', prompt: 'What is the capital of Germany?' },
|
|
180
174
|
],
|
|
181
175
|
});
|
|
182
176
|
|
|
@@ -195,6 +189,21 @@ for await (const item of getBatchResults({ model, batch })) {
|
|
|
195
189
|
}
|
|
196
190
|
```
|
|
197
191
|
|
|
192
|
+
`startTextBatch` returns a serializable batch reference. Persist this reference
|
|
193
|
+
to check the batch status or retrieve its results from another process. Results
|
|
194
|
+
can arrive in a different order from the input requests, so match each result by
|
|
195
|
+
its `id`.
|
|
196
|
+
|
|
197
|
+
#### Batch Limitations
|
|
198
|
+
|
|
199
|
+
<Note>
|
|
200
|
+
Anthropic Message Batches do not support the `speed` option or completion
|
|
201
|
+
webhooks. Explicit `anthropicBeta` values must be configured when starting the
|
|
202
|
+
batch rather than on an individual request. When you provide a `webhookUrl`,
|
|
203
|
+
the provider returns an unsupported warning and starts the batch without a
|
|
204
|
+
webhook.
|
|
205
|
+
</Note>
|
|
206
|
+
|
|
198
207
|
### Structured Outputs and Tool Input Streaming
|
|
199
208
|
|
|
200
209
|
Tool call streaming is enabled by default. You can opt out by setting the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/anthropic",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.44",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@ai-sdk/provider": "4.0.
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
38
|
+
"@ai-sdk/provider": "4.0.8",
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.32"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@ai-sdk/test-server": "2.0.1",
|
|
42
43
|
"@types/node": "22.19.19",
|
|
44
|
+
"@vercel/ai-tsconfig": "0.0.0",
|
|
43
45
|
"tsup": "^8.5.1",
|
|
44
46
|
"typescript": "5.8.3",
|
|
45
|
-
"zod": "3.25.76"
|
|
46
|
-
"@ai-sdk/test-server": "2.0.1",
|
|
47
|
-
"@vercel/ai-tsconfig": "0.0.0"
|
|
47
|
+
"zod": "3.25.76"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"zod": "^3.25.76 || ^4.1.8"
|
package/src/anthropic-api.ts
CHANGED
|
@@ -1368,6 +1368,10 @@ export const anthropicChunkSchema = lazySchema(() =>
|
|
|
1368
1368
|
error: z.object({
|
|
1369
1369
|
type: z.string(),
|
|
1370
1370
|
message: z.string(),
|
|
1371
|
+
code: z.union([z.string(), z.number()]).nullish(),
|
|
1372
|
+
statusCode: z.number().nullish(),
|
|
1373
|
+
isRetryable: z.boolean().nullish(),
|
|
1374
|
+
data: z.unknown().nullish(),
|
|
1371
1375
|
}),
|
|
1372
1376
|
}),
|
|
1373
1377
|
z.object({
|
|
@@ -20,9 +20,11 @@ import {
|
|
|
20
20
|
combineHeaders,
|
|
21
21
|
createEventSourceResponseHandler,
|
|
22
22
|
createJsonResponseHandler,
|
|
23
|
+
createProviderStreamError,
|
|
23
24
|
createToolNameMapping,
|
|
24
25
|
generateId,
|
|
25
26
|
isCustomReasoning,
|
|
27
|
+
isProviderStreamError,
|
|
26
28
|
mapReasoningToProviderBudget,
|
|
27
29
|
mapReasoningToProviderEffort,
|
|
28
30
|
parseProviderOptions,
|
|
@@ -69,6 +71,53 @@ import { CacheControlValidator } from './get-cache-control';
|
|
|
69
71
|
import { mapAnthropicStopReason } from './map-anthropic-stop-reason';
|
|
70
72
|
import { sanitizeJsonSchema } from './sanitize-json-schema';
|
|
71
73
|
|
|
74
|
+
function createAnthropicStreamError(error: {
|
|
75
|
+
message: string;
|
|
76
|
+
type: string;
|
|
77
|
+
code?: string | number | null;
|
|
78
|
+
statusCode?: number | null;
|
|
79
|
+
isRetryable?: boolean | null;
|
|
80
|
+
data?: unknown;
|
|
81
|
+
}) {
|
|
82
|
+
const inferredMetadata = getAnthropicStreamErrorMetadata(error.type);
|
|
83
|
+
|
|
84
|
+
return createProviderStreamError({
|
|
85
|
+
message: error.message,
|
|
86
|
+
type: error.type,
|
|
87
|
+
code: error.code ?? undefined,
|
|
88
|
+
statusCode: error.statusCode ?? inferredMetadata.statusCode,
|
|
89
|
+
isRetryable: error.isRetryable ?? inferredMetadata.isRetryable,
|
|
90
|
+
data: 'data' in error ? error.data : error,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getAnthropicStreamErrorMetadata(type: string): {
|
|
95
|
+
statusCode?: number;
|
|
96
|
+
isRetryable?: boolean;
|
|
97
|
+
} {
|
|
98
|
+
switch (type) {
|
|
99
|
+
case 'api_error':
|
|
100
|
+
return { statusCode: 500, isRetryable: true };
|
|
101
|
+
case 'overloaded_error':
|
|
102
|
+
return { statusCode: 529, isRetryable: true };
|
|
103
|
+
case 'rate_limit_error':
|
|
104
|
+
return { statusCode: 429, isRetryable: true };
|
|
105
|
+
case 'request_too_large':
|
|
106
|
+
return { statusCode: 413, isRetryable: false };
|
|
107
|
+
case 'authentication_error':
|
|
108
|
+
return { statusCode: 401, isRetryable: false };
|
|
109
|
+
case 'permission_error':
|
|
110
|
+
return { statusCode: 403, isRetryable: false };
|
|
111
|
+
case 'not_found_error':
|
|
112
|
+
return { statusCode: 404, isRetryable: false };
|
|
113
|
+
case 'billing_error':
|
|
114
|
+
case 'invalid_request_error':
|
|
115
|
+
return { statusCode: 400, isRetryable: false };
|
|
116
|
+
default:
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
72
121
|
function createCitationSource(
|
|
73
122
|
citation: Citation,
|
|
74
123
|
citationDocuments: Array<{
|
|
@@ -2662,7 +2711,10 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
|
|
|
2662
2711
|
}
|
|
2663
2712
|
|
|
2664
2713
|
case 'error': {
|
|
2665
|
-
controller.enqueue({
|
|
2714
|
+
controller.enqueue({
|
|
2715
|
+
type: 'error',
|
|
2716
|
+
error: createAnthropicStreamError(value.error),
|
|
2717
|
+
});
|
|
2666
2718
|
return;
|
|
2667
2719
|
}
|
|
2668
2720
|
|
|
@@ -2693,16 +2745,20 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
|
|
|
2693
2745
|
// We handle the case where the first chunk is an error here and transform
|
|
2694
2746
|
// it into an APICallError.
|
|
2695
2747
|
if (result.value?.type === 'error') {
|
|
2696
|
-
const error = result.value.error
|
|
2748
|
+
const error = result.value.error;
|
|
2749
|
+
|
|
2750
|
+
if (!isProviderStreamError(error)) {
|
|
2751
|
+
throw new Error('Expected a normalized Anthropic stream error');
|
|
2752
|
+
}
|
|
2697
2753
|
|
|
2698
2754
|
throw new APICallError({
|
|
2699
2755
|
message: error.message,
|
|
2700
2756
|
url,
|
|
2701
2757
|
requestBodyValues: body,
|
|
2702
|
-
statusCode: error.
|
|
2758
|
+
statusCode: error.statusCode ?? 500,
|
|
2703
2759
|
responseHeaders,
|
|
2704
|
-
responseBody: JSON.stringify(error),
|
|
2705
|
-
isRetryable: error.
|
|
2760
|
+
responseBody: JSON.stringify(error.data),
|
|
2761
|
+
isRetryable: error.isRetryable ?? false,
|
|
2706
2762
|
});
|
|
2707
2763
|
}
|
|
2708
2764
|
} finally {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
EmptyResponseBodyError,
|
|
3
2
|
InvalidArgumentError,
|
|
3
|
+
InvalidResponseDataError,
|
|
4
4
|
UnsupportedFunctionalityError,
|
|
5
5
|
type Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
|
|
6
6
|
type Experimental_BatchV4ItemResult as BatchV4ItemResult,
|
|
@@ -14,11 +14,12 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
combineHeaders,
|
|
16
16
|
convertAsyncIteratorToReadableStream,
|
|
17
|
+
createJsonLinesResponseHandler,
|
|
17
18
|
createJsonResponseHandler,
|
|
18
19
|
getFromApi,
|
|
19
20
|
lazySchema,
|
|
21
|
+
normalizeBatchRequestCounts,
|
|
20
22
|
normalizeHeaders,
|
|
21
|
-
parseJSON,
|
|
22
23
|
parseProviderOptions,
|
|
23
24
|
postJsonToApi,
|
|
24
25
|
resolve,
|
|
@@ -27,7 +28,6 @@ import {
|
|
|
27
28
|
WORKFLOW_SERIALIZE,
|
|
28
29
|
zodSchema,
|
|
29
30
|
type InferSchema,
|
|
30
|
-
type ResponseHandler,
|
|
31
31
|
} from '@ai-sdk/provider-utils';
|
|
32
32
|
import { z } from 'zod/v4';
|
|
33
33
|
import {
|
|
@@ -145,6 +145,7 @@ export class AnthropicMessagesBatchLanguageModel
|
|
|
145
145
|
providerOptions,
|
|
146
146
|
headers,
|
|
147
147
|
abortSignal,
|
|
148
|
+
webhookUrl,
|
|
148
149
|
}: Parameters<
|
|
149
150
|
BatchLanguageModelV4['experimental_doStartBatch']
|
|
150
151
|
>[0]): Promise<BatchV4StartResult> {
|
|
@@ -161,7 +162,19 @@ export class AnthropicMessagesBatchLanguageModel
|
|
|
161
162
|
custom_id: string;
|
|
162
163
|
params: Record<string, unknown>;
|
|
163
164
|
}> = [];
|
|
164
|
-
const batchWarnings: BatchV4StartResult['warnings'] =
|
|
165
|
+
const batchWarnings: BatchV4StartResult['warnings'] =
|
|
166
|
+
webhookUrl == null
|
|
167
|
+
? []
|
|
168
|
+
: [
|
|
169
|
+
{
|
|
170
|
+
warning: {
|
|
171
|
+
type: 'unsupported',
|
|
172
|
+
feature: 'webhookUrl',
|
|
173
|
+
details:
|
|
174
|
+
'The Anthropic Message Batches API does not support completion webhooks.',
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
];
|
|
165
178
|
|
|
166
179
|
for (const request of requests) {
|
|
167
180
|
const requestBetas = await getAnthropicBatchProviderBetas({
|
|
@@ -243,26 +256,28 @@ export class AnthropicMessagesBatchLanguageModel
|
|
|
243
256
|
}
|
|
244
257
|
|
|
245
258
|
if (batch.results_url == null) {
|
|
246
|
-
throw new
|
|
247
|
-
|
|
248
|
-
message: `Anthropic batch "${options.batchId}"
|
|
259
|
+
throw new InvalidResponseDataError({
|
|
260
|
+
data: batch,
|
|
261
|
+
message: `Anthropic batch "${options.batchId}" completed without batch output.`,
|
|
249
262
|
});
|
|
250
263
|
}
|
|
251
264
|
|
|
252
|
-
const { value:
|
|
265
|
+
const { value: lines } = await getFromApi({
|
|
253
266
|
url: batch.results_url,
|
|
254
267
|
validateUrl: true,
|
|
255
268
|
credentialedOrigin: this.config.baseURL,
|
|
256
269
|
trustedOrigin: this.config.baseURL,
|
|
257
270
|
headers: await this.getBatchHeaders(options.headers),
|
|
258
271
|
failedResponseHandler: anthropicFailedResponseHandler,
|
|
259
|
-
successfulResponseHandler:
|
|
272
|
+
successfulResponseHandler: createJsonLinesResponseHandler(
|
|
273
|
+
anthropicBatchResultLineSchema,
|
|
274
|
+
),
|
|
260
275
|
abortSignal: options.abortSignal,
|
|
261
276
|
fetch: this.config.fetch,
|
|
262
277
|
});
|
|
263
278
|
|
|
264
279
|
return convertAsyncIteratorToReadableStream(
|
|
265
|
-
this.iterateBatchResults(
|
|
280
|
+
this.iterateBatchResults(lines),
|
|
266
281
|
);
|
|
267
282
|
}
|
|
268
283
|
|
|
@@ -285,9 +300,9 @@ export class AnthropicMessagesBatchLanguageModel
|
|
|
285
300
|
}
|
|
286
301
|
|
|
287
302
|
private async *iterateBatchResults(
|
|
288
|
-
|
|
303
|
+
lines: AsyncIterable<AnthropicBatchResultLine>,
|
|
289
304
|
): AsyncGenerator<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
|
|
290
|
-
for await (const line of
|
|
305
|
+
for await (const line of lines) {
|
|
291
306
|
yield await convertAnthropicBatchResult(line);
|
|
292
307
|
}
|
|
293
308
|
}
|
|
@@ -440,24 +455,17 @@ function mapAnthropicBatchStatus(rawStatus: string): BatchV4Status['status'] {
|
|
|
440
455
|
function convertAnthropicRequestCounts(
|
|
441
456
|
counts: AnthropicBatchResponse['request_counts'],
|
|
442
457
|
): BatchV4Status['requestCounts'] | undefined {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if (values.some(value => !Number.isFinite(value) || value < 0)) {
|
|
452
|
-
return undefined;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return {
|
|
456
|
-
total: values.reduce((total, value) => total + value, 0),
|
|
458
|
+
return normalizeBatchRequestCounts({
|
|
459
|
+
total:
|
|
460
|
+
counts.processing +
|
|
461
|
+
counts.succeeded +
|
|
462
|
+
counts.errored +
|
|
463
|
+
counts.canceled +
|
|
464
|
+
counts.expired,
|
|
457
465
|
pending: counts.processing,
|
|
458
466
|
completed: counts.succeeded,
|
|
459
467
|
failed: counts.errored + counts.canceled + counts.expired,
|
|
460
|
-
};
|
|
468
|
+
});
|
|
461
469
|
}
|
|
462
470
|
|
|
463
471
|
async function convertAnthropicBatchResult(
|
|
@@ -517,7 +525,7 @@ async function convertAnthropicBatchResult(
|
|
|
517
525
|
message:
|
|
518
526
|
`Anthropic returned a "${unsupportedPart.type}" content block, ` +
|
|
519
527
|
'but tool content is not supported in AI SDK text batches.',
|
|
520
|
-
code: '
|
|
528
|
+
code: 'unsupported_content',
|
|
521
529
|
},
|
|
522
530
|
};
|
|
523
531
|
}
|
|
@@ -693,64 +701,3 @@ function mapAnthropicStopDetails(
|
|
|
693
701
|
: {}),
|
|
694
702
|
};
|
|
695
703
|
}
|
|
696
|
-
|
|
697
|
-
const rawStreamResponseHandler: ResponseHandler<
|
|
698
|
-
ReadableStream<Uint8Array>
|
|
699
|
-
> = async ({ response }) => {
|
|
700
|
-
if (response.body == null) {
|
|
701
|
-
throw new EmptyResponseBodyError();
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
return { value: response.body };
|
|
705
|
-
};
|
|
706
|
-
|
|
707
|
-
async function* parseJsonLines(
|
|
708
|
-
stream: ReadableStream<Uint8Array>,
|
|
709
|
-
): AsyncGenerator<AnthropicBatchResultLine> {
|
|
710
|
-
const reader = stream.getReader();
|
|
711
|
-
const decoder = new TextDecoder();
|
|
712
|
-
let buffer = '';
|
|
713
|
-
let finished = false;
|
|
714
|
-
|
|
715
|
-
try {
|
|
716
|
-
while (true) {
|
|
717
|
-
const { done, value } = await reader.read();
|
|
718
|
-
|
|
719
|
-
if (done) {
|
|
720
|
-
finished = true;
|
|
721
|
-
buffer += decoder.decode();
|
|
722
|
-
break;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
buffer += decoder.decode(value, { stream: true });
|
|
726
|
-
|
|
727
|
-
let lineEnd = buffer.indexOf('\n');
|
|
728
|
-
while (lineEnd !== -1) {
|
|
729
|
-
const line = buffer.slice(0, lineEnd).replace(/\r$/, '');
|
|
730
|
-
buffer = buffer.slice(lineEnd + 1);
|
|
731
|
-
|
|
732
|
-
if (line.trim().length > 0) {
|
|
733
|
-
yield await parseJSON({
|
|
734
|
-
text: line,
|
|
735
|
-
schema: anthropicBatchResultLineSchema,
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
lineEnd = buffer.indexOf('\n');
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
const finalLine = buffer.replace(/\r$/, '');
|
|
744
|
-
if (finalLine.trim().length > 0) {
|
|
745
|
-
yield await parseJSON({
|
|
746
|
-
text: finalLine,
|
|
747
|
-
schema: anthropicBatchResultLineSchema,
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
} finally {
|
|
751
|
-
if (!finished) {
|
|
752
|
-
await reader.cancel().catch(() => {});
|
|
753
|
-
}
|
|
754
|
-
reader.releaseLock();
|
|
755
|
-
}
|
|
756
|
-
}
|
|
@@ -22,6 +22,17 @@ interface AnthropicSkillsConfig {
|
|
|
22
22
|
fetch?: FetchFunction;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function encodePathSegment(value: string): string {
|
|
26
|
+
const encodedValue = encodeURIComponent(value);
|
|
27
|
+
|
|
28
|
+
// URL parsing normalizes both literal and percent-encoded dot segments.
|
|
29
|
+
return encodedValue === '.'
|
|
30
|
+
? '%252E'
|
|
31
|
+
: encodedValue === '..'
|
|
32
|
+
? '%252E%252E'
|
|
33
|
+
: encodedValue;
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
export class AnthropicSkills implements SkillsV4 {
|
|
26
37
|
readonly specificationVersion = 'v4';
|
|
27
38
|
|
|
@@ -47,7 +58,7 @@ export class AnthropicSkills implements SkillsV4 {
|
|
|
47
58
|
headers: Record<string, string | undefined>;
|
|
48
59
|
}): Promise<{ name?: string; description?: string }> {
|
|
49
60
|
const { value: versionResponse } = await getFromApi({
|
|
50
|
-
url: `${this.config.baseURL}/skills/${skillId}/versions/${version}`,
|
|
61
|
+
url: `${this.config.baseURL}/skills/${encodePathSegment(skillId)}/versions/${encodePathSegment(version)}`,
|
|
51
62
|
validateUrl: false,
|
|
52
63
|
headers,
|
|
53
64
|
failedResponseHandler: anthropicFailedResponseHandler,
|