Skip to content

Commit 02d0754

Browse files
feat(client): send retry count header (#533)
1 parent 369bac0 commit 02d0754

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

src/core.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,10 @@ export abstract class APIClient {
288288
return null;
289289
}
290290

291-
buildRequest<Req>(options: FinalRequestOptions<Req>): { req: RequestInit; url: string; timeout: number } {
291+
buildRequest<Req>(
292+
options: FinalRequestOptions<Req>,
293+
{ retryCount = 0 }: { retryCount?: number } = {},
294+
): { req: RequestInit; url: string; timeout: number } {
292295
const { method, path, query, headers: headers = {} } = options;
293296

294297
const body =
@@ -320,7 +323,7 @@ export abstract class APIClient {
320323
headers[this.idempotencyHeader] = options.idempotencyKey;
321324
}
322325

323-
const reqHeaders = this.buildHeaders({ options, headers, contentLength });
326+
const reqHeaders = this.buildHeaders({ options, headers, contentLength, retryCount });
324327

325328
const req: RequestInit = {
326329
method,
@@ -339,10 +342,12 @@ export abstract class APIClient {
339342
options,
340343
headers,
341344
contentLength,
345+
retryCount,
342346
}: {
343347
options: FinalRequestOptions;
344348
headers: Record<string, string | null | undefined>;
345349
contentLength: string | null | undefined;
350+
retryCount: number;
346351
}): Record<string, string> {
347352
const reqHeaders: Record<string, string> = {};
348353
if (contentLength) {
@@ -358,6 +363,8 @@ export abstract class APIClient {
358363
delete reqHeaders['content-type'];
359364
}
360365

366+
reqHeaders['x-stainless-retry-count'] = String(retryCount);
367+
361368
this.validateHeaders(reqHeaders, headers);
362369

363370
return reqHeaders;
@@ -409,13 +416,14 @@ export abstract class APIClient {
409416
retriesRemaining: number | null,
410417
): Promise<APIResponseProps> {
411418
const options = await optionsInput;
419+
const maxRetries = options.maxRetries ?? this.maxRetries;
412420
if (retriesRemaining == null) {
413-
retriesRemaining = options.maxRetries ?? this.maxRetries;
421+
retriesRemaining = maxRetries;
414422
}
415423

416424
await this.prepareOptions(options);
417425

418-
const { req, url, timeout } = this.buildRequest(options);
426+
const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining });
419427

420428
await this.prepareRequest(req, { url, options });
421429

tests/index.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,31 @@ describe('retries', () => {
247247
expect(count).toEqual(3);
248248
});
249249

250+
test('retry count header', async () => {
251+
let count = 0;
252+
let capturedRequest: RequestInit | undefined;
253+
const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
254+
count++;
255+
if (count <= 2) {
256+
return new Response(undefined, {
257+
status: 429,
258+
headers: {
259+
'Retry-After': '0.1',
260+
},
261+
});
262+
}
263+
capturedRequest = init;
264+
return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
265+
};
266+
267+
const client = new Anthropic({ apiKey: 'my-anthropic-api-key', fetch: testFetch, maxRetries: 4 });
268+
269+
expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
270+
271+
expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toEqual('2');
272+
expect(count).toEqual(3);
273+
});
274+
250275
test('retry on 429 with retry-after', async () => {
251276
let count = 0;
252277
const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {

0 commit comments

Comments
 (0)