Skip to content

Commit ce01165

Browse files
feat(client): allow overriding retry count header (#1098)
1 parent 00c93cd commit ce01165

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

src/core.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,11 @@ export abstract class APIClient {
363363
delete reqHeaders['content-type'];
364364
}
365365

366-
reqHeaders['x-stainless-retry-count'] = String(retryCount);
366+
// Don't set the retry count header if it was already set or removed by the caller. We check `headers`,
367+
// which can contain nulls, instead of `reqHeaders` to account for the removal case.
368+
if (getHeader(headers, 'x-stainless-retry-count') === undefined) {
369+
reqHeaders['x-stainless-retry-count'] = String(retryCount);
370+
}
367371

368372
this.validateHeaders(reqHeaders, headers);
369373

@@ -1144,7 +1148,15 @@ export const isHeadersProtocol = (headers: any): headers is HeadersProtocol => {
11441148
return typeof headers?.get === 'function';
11451149
};
11461150

1147-
export const getRequiredHeader = (headers: HeadersLike, header: string): string => {
1151+
export const getRequiredHeader = (headers: HeadersLike | Headers, header: string): string => {
1152+
const foundHeader = getHeader(headers, header);
1153+
if (foundHeader === undefined) {
1154+
throw new Error(`Could not find ${header} header`);
1155+
}
1156+
return foundHeader;
1157+
};
1158+
1159+
export const getHeader = (headers: HeadersLike | Headers, header: string): string | undefined => {
11481160
const lowerCasedHeader = header.toLowerCase();
11491161
if (isHeadersProtocol(headers)) {
11501162
// to deal with the case where the header looks like Stainless-Event-Id
@@ -1170,7 +1182,7 @@ export const getRequiredHeader = (headers: HeadersLike, header: string): string
11701182
}
11711183
}
11721184

1173-
throw new Error(`Could not find ${header} header`);
1185+
return undefined;
11741186
};
11751187

11761188
/**

tests/index.test.ts

+58
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,64 @@ describe('retries', () => {
266266
expect(count).toEqual(3);
267267
});
268268

269+
test('omit retry count header', async () => {
270+
let count = 0;
271+
let capturedRequest: RequestInit | undefined;
272+
const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
273+
count++;
274+
if (count <= 2) {
275+
return new Response(undefined, {
276+
status: 429,
277+
headers: {
278+
'Retry-After': '0.1',
279+
},
280+
});
281+
}
282+
capturedRequest = init;
283+
return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
284+
};
285+
const client = new OpenAI({ apiKey: 'My API Key', fetch: testFetch, maxRetries: 4 });
286+
287+
expect(
288+
await client.request({
289+
path: '/foo',
290+
method: 'get',
291+
headers: { 'X-Stainless-Retry-Count': null },
292+
}),
293+
).toEqual({ a: 1 });
294+
295+
expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
296+
});
297+
298+
test('overwrite retry count header', async () => {
299+
let count = 0;
300+
let capturedRequest: RequestInit | undefined;
301+
const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
302+
count++;
303+
if (count <= 2) {
304+
return new Response(undefined, {
305+
status: 429,
306+
headers: {
307+
'Retry-After': '0.1',
308+
},
309+
});
310+
}
311+
capturedRequest = init;
312+
return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
313+
};
314+
const client = new OpenAI({ apiKey: 'My API Key', fetch: testFetch, maxRetries: 4 });
315+
316+
expect(
317+
await client.request({
318+
path: '/foo',
319+
method: 'get',
320+
headers: { 'X-Stainless-Retry-Count': '42' },
321+
}),
322+
).toEqual({ a: 1 });
323+
324+
expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toBe('42');
325+
});
326+
269327
test('retry on 429 with retry-after', async () => {
270328
let count = 0;
271329
const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {

0 commit comments

Comments
 (0)