Skip to content

Commit 1c404b2

Browse files
RobertCraigiestainless-app[bot]
authored andcommitted
feat(api): add message batches api
For more information see https://anthropic.com/news/message-batches-api
1 parent d225d1d commit 1c404b2

File tree

19 files changed

+2187
-30
lines changed

19 files changed

+2187
-30
lines changed

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
configured_endpoints: 3
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic-286f00929e2a4d28d991e6a7e660fa801dca7ec91d8ecb2fc17654bb8173eb0d.yml
1+
configured_endpoints: 9
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic-aedee7570aa925baba404fc5bd3c8c1fffe8845517e492751db9b175c5cae9da.yml

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,51 @@ Streaming with `client.messages.stream(...)` exposes [various helpers for your c
132132

133133
Alternatively, you can use `client.messages.create({ ..., stream: true })` which only returns an async iterable of the events in the stream and thus uses less memory (it does not build up a final message object for you).
134134

135+
## Message Batches
136+
137+
This SDK provides beta support for the [Message Batches API](https://docs.anthropic.com/en/docs/build-with-claude/message-batches) under the `client.beta.messages.batches` namespace.
138+
139+
### Creating a batch
140+
141+
Message Batches take the exact same request params as the standard Messages API:
142+
143+
```ts
144+
await anthropic.beta.messages.batches.create({
145+
requests: [
146+
{
147+
custom_id: 'my-first-request',
148+
params: {
149+
model: 'claude-3-5-sonnet-20240620',
150+
max_tokens: 1024,
151+
messages: [{ role: 'user', content: 'Hello, world' }],
152+
},
153+
},
154+
{
155+
custom_id: 'my-second-request',
156+
params: {
157+
model: 'claude-3-5-sonnet-20240620',
158+
max_tokens: 1024,
159+
messages: [{ role: 'user', content: 'Hi again, friend' }],
160+
},
161+
},
162+
],
163+
});
164+
```
165+
166+
167+
### Getting results from a batch
168+
169+
Once a Message Batch has been processed, indicated by `.processing_status === 'ended'`, you can access the results with `.batches.results()`
170+
171+
```ts
172+
const results = await anthropic.beta.messages.batches.results(batch_id);
173+
for await (const entry of results) {
174+
if (entry.result.type === 'succeeded') {
175+
console.log(entry.result.message.content)
176+
}
177+
}
178+
```
179+
135180
## Tool use beta
136181

137182
This SDK provides beta support for tool use, aka function calling. More details can be found in [the documentation](https://docs.anthropic.com/claude/docs/tool-use).
@@ -224,6 +269,37 @@ On timeout, an `APIConnectionTimeoutError` is thrown.
224269

225270
Note that requests which time out will be [retried twice by default](#retries).
226271

272+
## Auto-pagination
273+
274+
List methods in the Anthropic API are paginated.
275+
You can use `for await … of` syntax to iterate through items across all pages:
276+
277+
```ts
278+
async function fetchAllBetaMessagesBatches(params) {
279+
const allBetaMessagesBatches = [];
280+
// Automatically fetches more pages as needed.
281+
for await (const betaMessageBatch of client.beta.messages.batches.list({ limit: 20 })) {
282+
allBetaMessagesBatches.push(betaMessageBatch);
283+
}
284+
return allBetaMessagesBatches;
285+
}
286+
```
287+
288+
Alternatively, you can make request a single page at a time:
289+
290+
```ts
291+
let page = await client.beta.messages.batches.list({ limit: 20 });
292+
for (const betaMessageBatch of page.data) {
293+
console.log(betaMessageBatch);
294+
}
295+
296+
// Convenience methods are provided for manually paginating:
297+
while (page.hasNextPage()) {
298+
page = page.getNextPage();
299+
// ...
300+
}
301+
```
302+
227303
## Default Headers
228304

229305
We automatically send the `anthropic-version` header set to `2023-06-01`.

api.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,77 @@ Methods:
4646

4747
# Beta
4848

49+
Types:
50+
51+
- <code><a href="./src/resources/beta/beta.ts">AnthropicBeta</a></code>
52+
- <code><a href="./src/resources/beta/beta.ts">BetaAPIError</a></code>
53+
- <code><a href="./src/resources/beta/beta.ts">BetaAuthenticationError</a></code>
54+
- <code><a href="./src/resources/beta/beta.ts">BetaError</a></code>
55+
- <code><a href="./src/resources/beta/beta.ts">BetaErrorResponse</a></code>
56+
- <code><a href="./src/resources/beta/beta.ts">BetaInvalidRequestError</a></code>
57+
- <code><a href="./src/resources/beta/beta.ts">BetaNotFoundError</a></code>
58+
- <code><a href="./src/resources/beta/beta.ts">BetaOverloadedError</a></code>
59+
- <code><a href="./src/resources/beta/beta.ts">BetaPermissionError</a></code>
60+
- <code><a href="./src/resources/beta/beta.ts">BetaRateLimitError</a></code>
61+
62+
## Messages
63+
64+
Types:
65+
66+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaCacheControlEphemeral</a></code>
67+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaContentBlock</a></code>
68+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaContentBlockParam</a></code>
69+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaImageBlockParam</a></code>
70+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaInputJSONDelta</a></code>
71+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaMessage</a></code>
72+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaMessageDeltaUsage</a></code>
73+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaMessageParam</a></code>
74+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaMetadata</a></code>
75+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaRawContentBlockDeltaEvent</a></code>
76+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaRawContentBlockStartEvent</a></code>
77+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaRawContentBlockStopEvent</a></code>
78+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaRawMessageDeltaEvent</a></code>
79+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaRawMessageStartEvent</a></code>
80+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaRawMessageStopEvent</a></code>
81+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaRawMessageStreamEvent</a></code>
82+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaTextBlock</a></code>
83+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaTextBlockParam</a></code>
84+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaTextDelta</a></code>
85+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaTool</a></code>
86+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaToolChoice</a></code>
87+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaToolChoiceAny</a></code>
88+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaToolChoiceAuto</a></code>
89+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaToolChoiceTool</a></code>
90+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaToolResultBlockParam</a></code>
91+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaToolUseBlock</a></code>
92+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaToolUseBlockParam</a></code>
93+
- <code><a href="./src/resources/beta/messages/messages.ts">BetaUsage</a></code>
94+
95+
Methods:
96+
97+
- <code title="post /v1/messages?beta=true">client.beta.messages.<a href="./src/resources/beta/messages/messages.ts">create</a>({ ...params }) -> BetaMessage</code>
98+
99+
### Batches
100+
101+
Types:
102+
103+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatch</a></code>
104+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatchCanceledResult</a></code>
105+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatchErroredResult</a></code>
106+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatchExpiredResult</a></code>
107+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatchIndividualResponse</a></code>
108+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatchRequestCounts</a></code>
109+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatchResult</a></code>
110+
- <code><a href="./src/resources/beta/messages/batches.ts">BetaMessageBatchSucceededResult</a></code>
111+
112+
Methods:
113+
114+
- <code title="post /v1/messages/batches?beta=true">client.beta.messages.batches.<a href="./src/resources/beta/messages/batches.ts">create</a>({ ...params }) -> BetaMessageBatch</code>
115+
- <code title="get /v1/messages/batches/{message_batch_id}?beta=true">client.beta.messages.batches.<a href="./src/resources/beta/messages/batches.ts">retrieve</a>(messageBatchId, { ...params }) -> BetaMessageBatch</code>
116+
- <code title="get /v1/messages/batches?beta=true">client.beta.messages.batches.<a href="./src/resources/beta/messages/batches.ts">list</a>({ ...params }) -> BetaMessageBatchesPage</code>
117+
- <code title="post /v1/messages/batches/{message_batch_id}/cancel?beta=true">client.beta.messages.batches.<a href="./src/resources/beta/messages/batches.ts">cancel</a>(messageBatchId, { ...params }) -> BetaMessageBatch</code>
118+
- <code title="get /v1/messages/batches/{message_batch_id}/results?beta=true">client.beta.messages.batches.<a href="./src/resources/beta/messages/batches.ts">results</a>(messageBatchId, { ...params }) -> Response</code>
119+
49120
## PromptCaching
50121

51122
### Messages

examples/batch-results.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Anthropic from '@anthropic-ai/sdk/index';
2+
3+
const anthropic = new Anthropic();
4+
5+
async function main() {
6+
const batch_id = process.argv[2];
7+
if (!batch_id) {
8+
throw new Error('must specify a message batch ID, `yarn tsn examples/batch-results.ts msgbatch_123`');
9+
}
10+
11+
console.log(`fetching results for ${batch_id}`);
12+
13+
const results = await anthropic.beta.messages.batches.results(batch_id);
14+
15+
for await (const result of results) {
16+
console.log(result);
17+
}
18+
}
19+
20+
main();

src/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as Errors from './error';
44
import * as Uploads from './uploads';
55
import { type Agent } from './_shims/index';
66
import * as Core from './core';
7+
import * as Pagination from './pagination';
78
import * as API from './resources/index';
89

910
export interface ClientOptions {
@@ -250,6 +251,10 @@ export import fileFromPath = Uploads.fileFromPath;
250251
export namespace Anthropic {
251252
export import RequestOptions = Core.RequestOptions;
252253

254+
export import Page = Pagination.Page;
255+
export import PageParams = Pagination.PageParams;
256+
export import PageResponse = Pagination.PageResponse;
257+
253258
export import Completions = API.Completions;
254259
export import Completion = API.Completion;
255260
export import CompletionCreateParams = API.CompletionCreateParams;
@@ -297,6 +302,16 @@ export namespace Anthropic {
297302
export import MessageStreamParams = API.MessageStreamParams;
298303

299304
export import Beta = API.Beta;
305+
export import AnthropicBeta = API.AnthropicBeta;
306+
export import BetaAPIError = API.BetaAPIError;
307+
export import BetaAuthenticationError = API.BetaAuthenticationError;
308+
export import BetaError = API.BetaError;
309+
export import BetaErrorResponse = API.BetaErrorResponse;
310+
export import BetaInvalidRequestError = API.BetaInvalidRequestError;
311+
export import BetaNotFoundError = API.BetaNotFoundError;
312+
export import BetaOverloadedError = API.BetaOverloadedError;
313+
export import BetaPermissionError = API.BetaPermissionError;
314+
export import BetaRateLimitError = API.BetaRateLimitError;
300315
}
301316

302317
export default Anthropic;

src/internal/decoders/jsonl.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { AnthropicError } from '../../error';
2+
import { readableStreamAsyncIterable } from '../../streaming';
3+
import { type Response } from '../../_shims/index';
4+
import { LineDecoder, type Bytes } from './line';
5+
6+
export class JSONLDecoder<T> {
7+
controller: AbortController;
8+
9+
constructor(
10+
private iterator: AsyncIterableIterator<Bytes>,
11+
controller: AbortController,
12+
) {
13+
this.controller = controller;
14+
}
15+
16+
private async *decoder(): AsyncIterator<T, any, undefined> {
17+
const lineDecoder = new LineDecoder();
18+
for await (const chunk of this.iterator) {
19+
for (const line of lineDecoder.decode(chunk)) {
20+
yield JSON.parse(line);
21+
}
22+
}
23+
24+
for (const line of lineDecoder.flush()) {
25+
yield JSON.parse(line);
26+
}
27+
}
28+
29+
[Symbol.asyncIterator](): AsyncIterator<T> {
30+
return this.decoder();
31+
}
32+
33+
static fromResponse<T>(response: Response, controller: AbortController): JSONLDecoder<T> {
34+
if (!response.body) {
35+
controller.abort();
36+
throw new AnthropicError(`Attempted to iterate over a response with no body`);
37+
}
38+
39+
return new JSONLDecoder(readableStreamAsyncIterable<Bytes>(response.body), controller);
40+
}
41+
}

src/internal/decoders/line.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AnthropicError } from '../../error';
22

3-
type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined;
3+
export type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined;
44

55
/**
66
* A re-implementation of httpx's `LineDecoder` in Python that handles incrementally

src/pagination.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
import { AbstractPage, Response, APIClient, FinalRequestOptions, PageInfo } from './core';
4+
5+
export interface PageResponse<Item> {
6+
data: Array<Item>;
7+
8+
has_more: boolean;
9+
10+
first_id: string | null;
11+
12+
last_id: string | null;
13+
}
14+
15+
export interface PageParams {
16+
/**
17+
* Number of items per page.
18+
*/
19+
limit?: number;
20+
21+
before_id?: string;
22+
23+
after_id?: string;
24+
}
25+
26+
export class Page<Item> extends AbstractPage<Item> implements PageResponse<Item> {
27+
data: Array<Item>;
28+
29+
has_more: boolean;
30+
31+
first_id: string | null;
32+
33+
last_id: string | null;
34+
35+
constructor(client: APIClient, response: Response, body: PageResponse<Item>, options: FinalRequestOptions) {
36+
super(client, response, body, options);
37+
38+
this.data = body.data || [];
39+
this.has_more = body.has_more || false;
40+
this.first_id = body.first_id || null;
41+
this.last_id = body.last_id || null;
42+
}
43+
44+
getPaginatedItems(): Item[] {
45+
return this.data ?? [];
46+
}
47+
48+
// @deprecated Please use `nextPageInfo()` instead
49+
nextPageParams(): Partial<PageParams> | null {
50+
const info = this.nextPageInfo();
51+
if (!info) return null;
52+
if ('params' in info) return info.params;
53+
const params = Object.fromEntries(info.url.searchParams);
54+
if (!Object.keys(params).length) return null;
55+
return params;
56+
}
57+
58+
nextPageInfo(): PageInfo | null {
59+
if ((this.options.query as Record<string, unknown>)?.['before_id']) {
60+
// in reverse
61+
const firstId = this.first_id;
62+
if (!firstId) {
63+
return null;
64+
}
65+
66+
return {
67+
params: {
68+
before_id: firstId,
69+
},
70+
};
71+
}
72+
73+
const cursor = this.last_id;
74+
if (!cursor) {
75+
return null;
76+
}
77+
78+
return {
79+
params: {
80+
after_id: cursor,
81+
},
82+
};
83+
}
84+
}

0 commit comments

Comments
 (0)