@ai-sdk/anthropic 4.0.42 → 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 +19 -0
- package/dist/index.js +79 -83
- 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 +2 -2
- package/src/anthropic-api.ts +4 -0
- package/src/anthropic-language-model.ts +61 -5
- package/src/anthropic-messages-batch.ts +22 -88
- 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,
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@ai-sdk/provider": "4.0.8",
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.32"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.1",
|
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 {
|
|
@@ -256,26 +256,28 @@ export class AnthropicMessagesBatchLanguageModel
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
if (batch.results_url == null) {
|
|
259
|
-
throw new
|
|
260
|
-
|
|
261
|
-
message: `Anthropic batch "${options.batchId}"
|
|
259
|
+
throw new InvalidResponseDataError({
|
|
260
|
+
data: batch,
|
|
261
|
+
message: `Anthropic batch "${options.batchId}" completed without batch output.`,
|
|
262
262
|
});
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
const { value:
|
|
265
|
+
const { value: lines } = await getFromApi({
|
|
266
266
|
url: batch.results_url,
|
|
267
267
|
validateUrl: true,
|
|
268
268
|
credentialedOrigin: this.config.baseURL,
|
|
269
269
|
trustedOrigin: this.config.baseURL,
|
|
270
270
|
headers: await this.getBatchHeaders(options.headers),
|
|
271
271
|
failedResponseHandler: anthropicFailedResponseHandler,
|
|
272
|
-
successfulResponseHandler:
|
|
272
|
+
successfulResponseHandler: createJsonLinesResponseHandler(
|
|
273
|
+
anthropicBatchResultLineSchema,
|
|
274
|
+
),
|
|
273
275
|
abortSignal: options.abortSignal,
|
|
274
276
|
fetch: this.config.fetch,
|
|
275
277
|
});
|
|
276
278
|
|
|
277
279
|
return convertAsyncIteratorToReadableStream(
|
|
278
|
-
this.iterateBatchResults(
|
|
280
|
+
this.iterateBatchResults(lines),
|
|
279
281
|
);
|
|
280
282
|
}
|
|
281
283
|
|
|
@@ -298,9 +300,9 @@ export class AnthropicMessagesBatchLanguageModel
|
|
|
298
300
|
}
|
|
299
301
|
|
|
300
302
|
private async *iterateBatchResults(
|
|
301
|
-
|
|
303
|
+
lines: AsyncIterable<AnthropicBatchResultLine>,
|
|
302
304
|
): AsyncGenerator<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
|
|
303
|
-
for await (const line of
|
|
305
|
+
for await (const line of lines) {
|
|
304
306
|
yield await convertAnthropicBatchResult(line);
|
|
305
307
|
}
|
|
306
308
|
}
|
|
@@ -453,24 +455,17 @@ function mapAnthropicBatchStatus(rawStatus: string): BatchV4Status['status'] {
|
|
|
453
455
|
function convertAnthropicRequestCounts(
|
|
454
456
|
counts: AnthropicBatchResponse['request_counts'],
|
|
455
457
|
): BatchV4Status['requestCounts'] | undefined {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (values.some(value => !Number.isFinite(value) || value < 0)) {
|
|
465
|
-
return undefined;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
return {
|
|
469
|
-
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,
|
|
470
465
|
pending: counts.processing,
|
|
471
466
|
completed: counts.succeeded,
|
|
472
467
|
failed: counts.errored + counts.canceled + counts.expired,
|
|
473
|
-
};
|
|
468
|
+
});
|
|
474
469
|
}
|
|
475
470
|
|
|
476
471
|
async function convertAnthropicBatchResult(
|
|
@@ -530,7 +525,7 @@ async function convertAnthropicBatchResult(
|
|
|
530
525
|
message:
|
|
531
526
|
`Anthropic returned a "${unsupportedPart.type}" content block, ` +
|
|
532
527
|
'but tool content is not supported in AI SDK text batches.',
|
|
533
|
-
code: '
|
|
528
|
+
code: 'unsupported_content',
|
|
534
529
|
},
|
|
535
530
|
};
|
|
536
531
|
}
|
|
@@ -706,64 +701,3 @@ function mapAnthropicStopDetails(
|
|
|
706
701
|
: {}),
|
|
707
702
|
};
|
|
708
703
|
}
|
|
709
|
-
|
|
710
|
-
const rawStreamResponseHandler: ResponseHandler<
|
|
711
|
-
ReadableStream<Uint8Array>
|
|
712
|
-
> = async ({ response }) => {
|
|
713
|
-
if (response.body == null) {
|
|
714
|
-
throw new EmptyResponseBodyError();
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
return { value: response.body };
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
async function* parseJsonLines(
|
|
721
|
-
stream: ReadableStream<Uint8Array>,
|
|
722
|
-
): AsyncGenerator<AnthropicBatchResultLine> {
|
|
723
|
-
const reader = stream.getReader();
|
|
724
|
-
const decoder = new TextDecoder();
|
|
725
|
-
let buffer = '';
|
|
726
|
-
let finished = false;
|
|
727
|
-
|
|
728
|
-
try {
|
|
729
|
-
while (true) {
|
|
730
|
-
const { done, value } = await reader.read();
|
|
731
|
-
|
|
732
|
-
if (done) {
|
|
733
|
-
finished = true;
|
|
734
|
-
buffer += decoder.decode();
|
|
735
|
-
break;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
buffer += decoder.decode(value, { stream: true });
|
|
739
|
-
|
|
740
|
-
let lineEnd = buffer.indexOf('\n');
|
|
741
|
-
while (lineEnd !== -1) {
|
|
742
|
-
const line = buffer.slice(0, lineEnd).replace(/\r$/, '');
|
|
743
|
-
buffer = buffer.slice(lineEnd + 1);
|
|
744
|
-
|
|
745
|
-
if (line.trim().length > 0) {
|
|
746
|
-
yield await parseJSON({
|
|
747
|
-
text: line,
|
|
748
|
-
schema: anthropicBatchResultLineSchema,
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
lineEnd = buffer.indexOf('\n');
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
const finalLine = buffer.replace(/\r$/, '');
|
|
757
|
-
if (finalLine.trim().length > 0) {
|
|
758
|
-
yield await parseJSON({
|
|
759
|
-
text: finalLine,
|
|
760
|
-
schema: anthropicBatchResultLineSchema,
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
} finally {
|
|
764
|
-
if (!finished) {
|
|
765
|
-
await reader.cancel().catch(() => {});
|
|
766
|
-
}
|
|
767
|
-
reader.releaseLock();
|
|
768
|
-
}
|
|
769
|
-
}
|
|
@@ -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,
|