Skip to content

Commit 9ac1170

Browse files
feat(client): send retry count header (#1087)
1 parent 609827e commit 9ac1170

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
@@ -241,6 +241,31 @@ describe('retries', () => {
241241
expect(count).toEqual(3);
242242
});
243243

244+
test('retry count header', async () => {
245+
let count = 0;
246+
let capturedRequest: RequestInit | undefined;
247+
const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
248+
count++;
249+
if (count <= 2) {
250+
return new Response(undefined, {
251+
status: 429,
252+
headers: {
253+
'Retry-After': '0.1',
254+
},
255+
});
256+
}
257+
capturedRequest = init;
258+
return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
259+
};
260+
261+
const client = new OpenAI({ apiKey: 'My API Key', fetch: testFetch, maxRetries: 4 });
262+
263+
expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
264+
265+
expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toEqual('2');
266+
expect(count).toEqual(3);
267+
});
268+
244269
test('retry on 429 with retry-after', async () => {
245270
let count = 0;
246271
const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {

0 commit comments

Comments
 (0)