diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d2c18a540..9721abd6f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.8.3" + ".": "5.8.4" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eab53103..e6ea7bf52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 5.8.4 (2025-07-10) + +Full Changelog: [v5.8.3...v5.8.4](https://github.com/openai/openai-node/compare/v5.8.3...v5.8.4) + +### Chores + +* **internal:** bump undici version in tests ([6f38b80](https://github.com/openai/openai-node/commit/6f38b809a69b6ab3fcbf1235e57bdb8912024ab3)) +* make some internal functions async ([841940d](https://github.com/openai/openai-node/commit/841940d2ae036191b456e8398fbcb1f24c6e4deb)) + ## 5.8.3 (2025-07-08) Full Changelog: [v5.8.2...v5.8.3](https://github.com/openai/openai-node/compare/v5.8.2...v5.8.3) diff --git a/ecosystem-tests/node-ts-cjs/package-lock.json b/ecosystem-tests/node-ts-cjs/package-lock.json index dcbda28e4..7179bad95 100644 --- a/ecosystem-tests/node-ts-cjs/package-lock.json +++ b/ecosystem-tests/node-ts-cjs/package-lock.json @@ -11,7 +11,7 @@ "formdata-node": "^4.4.1", "tsconfig-paths": "^4.0.0", "typescript": "^5.7.3", - "undici": "^7.2.0" + "undici": "^7.10.0" }, "devDependencies": { "@types/node": "^20.14.8", @@ -4254,9 +4254,9 @@ } }, "node_modules/undici": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.2.0.tgz", - "integrity": "sha512-klt+0S55GBViA9nsq48/NSCo4YX5mjydjypxD7UmHh/brMu8h/Mhd/F7qAeoH2NOO8SDTk6kjnTFc4WpzmfYpQ==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", + "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", "license": "MIT", "engines": { "node": ">=20.18.1" diff --git a/ecosystem-tests/node-ts-cjs/package.json b/ecosystem-tests/node-ts-cjs/package.json index 7946e8eae..194e9ca62 100644 --- a/ecosystem-tests/node-ts-cjs/package.json +++ b/ecosystem-tests/node-ts-cjs/package.json @@ -11,7 +11,7 @@ "formdata-node": "^4.4.1", "tsconfig-paths": "^4.0.0", "typescript": "^5.7.3", - "undici": "^7.2.0" + "undici": "^7.10.0" }, "devDependencies": { "@types/node": "^20.14.8", diff --git a/jsr.json b/jsr.json index f80334123..20dc50bb8 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@openai/openai", - "version": "5.8.3", + "version": "5.8.4", "exports": { ".": "./index.ts", "./helpers/zod": "./helpers/zod.ts", diff --git a/package.json b/package.json index 558752b05..bd2058b08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openai", - "version": "5.8.3", + "version": "5.8.4", "description": "The official TypeScript library for the OpenAI API", "author": "OpenAI ", "types": "dist/index.d.ts", diff --git a/src/azure.ts b/src/azure.ts index 48ada3a28..490b82b9f 100644 --- a/src/azure.ts +++ b/src/azure.ts @@ -125,10 +125,10 @@ export class AzureOpenAI extends OpenAI { this.deploymentName = deployment; } - override buildRequest( + override async buildRequest( options: FinalRequestOptions, props: { retryCount?: number } = {}, - ): { req: RequestInit & { headers: Headers }; url: string; timeout: number } { + ): Promise<{ req: RequestInit & { headers: Headers }; url: string; timeout: number }> { if (_deployments_endpoints.has(options.path) && options.method === 'post' && options.body !== undefined) { if (!isObj(options.body)) { throw new Error('Expected request body to be an object'); @@ -154,7 +154,7 @@ export class AzureOpenAI extends OpenAI { return undefined; } - protected override authHeaders(opts: FinalRequestOptions): NullableHeaders | undefined { + protected override async authHeaders(opts: FinalRequestOptions): Promise { return; } diff --git a/src/client.ts b/src/client.ts index 113b49936..74ca1c51d 100644 --- a/src/client.ts +++ b/src/client.ts @@ -370,7 +370,7 @@ export class OpenAI { * Create a new client instance re-using the same options given to the current client with optional overriding. */ withOptions(options: Partial): this { - return new (this.constructor as any as new (props: ClientOptions) => typeof this)({ + const client = new (this.constructor as any as new (props: ClientOptions) => typeof this)({ ...this._options, baseURL: this.baseURL, maxRetries: this.maxRetries, @@ -385,6 +385,7 @@ export class OpenAI { webhookSecret: this.webhookSecret, ...options, }); + return client; } /** @@ -402,7 +403,7 @@ export class OpenAI { return; } - protected authHeaders(opts: FinalRequestOptions): NullableHeaders | undefined { + protected async authHeaders(opts: FinalRequestOptions): Promise { return buildHeaders([{ Authorization: `Bearer ${this.apiKey}` }]); } @@ -518,7 +519,9 @@ export class OpenAI { await this.prepareOptions(options); - const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining }); + const { req, url, timeout } = await this.buildRequest(options, { + retryCount: maxRetries - retriesRemaining, + }); await this.prepareRequest(req, { url, options }); @@ -600,7 +603,7 @@ export class OpenAI { } with status ${response.status} in ${headersTime - startTime}ms`; if (!response.ok) { - const shouldRetry = this.shouldRetry(response); + const shouldRetry = await this.shouldRetry(response); if (retriesRemaining && shouldRetry) { const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; @@ -718,7 +721,7 @@ export class OpenAI { } } - private shouldRetry(response: Response): boolean { + private async shouldRetry(response: Response): Promise { // Note this is not a standard header. const shouldRetryHeader = response.headers.get('x-should-retry'); @@ -795,10 +798,10 @@ export class OpenAI { return sleepSeconds * jitter * 1000; } - buildRequest( + async buildRequest( inputOptions: FinalRequestOptions, { retryCount = 0 }: { retryCount?: number } = {}, - ): { req: FinalizedRequestInit; url: string; timeout: number } { + ): Promise<{ req: FinalizedRequestInit; url: string; timeout: number }> { const options = { ...inputOptions }; const { method, path, query, defaultBaseURL } = options; @@ -806,7 +809,7 @@ export class OpenAI { if ('timeout' in options) validatePositiveInteger('timeout', options.timeout); options.timeout = options.timeout ?? this.timeout; const { bodyHeaders, body } = this.buildBody({ options }); - const reqHeaders = this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount }); + const reqHeaders = await this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount }); const req: FinalizedRequestInit = { method, @@ -822,7 +825,7 @@ export class OpenAI { return { req, url, timeout: options.timeout }; } - private buildHeaders({ + private async buildHeaders({ options, method, bodyHeaders, @@ -832,7 +835,7 @@ export class OpenAI { method: HTTPMethod; bodyHeaders: HeadersLike; retryCount: number; - }): Headers { + }): Promise { let idempotencyHeaders: HeadersLike = {}; if (this.idempotencyHeader && method !== 'get') { if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey(); @@ -850,7 +853,7 @@ export class OpenAI { 'OpenAI-Organization': this.organization, 'OpenAI-Project': this.project, }, - this.authHeaders(options), + await this.authHeaders(options), this._options.defaultHeaders, bodyHeaders, options.headers, diff --git a/src/version.ts b/src/version.ts index 656ef69ab..1d7e6fe66 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '5.8.3'; // x-release-please-version +export const VERSION = '5.8.4'; // x-release-please-version diff --git a/tests/index.test.ts b/tests/index.test.ts index e61902eea..c8b4b819c 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -26,13 +26,13 @@ describe('instantiate client', () => { apiKey: 'My API Key', }); - test('they are used in the request', () => { - const { req } = client.buildRequest({ path: '/foo', method: 'post' }); + test('they are used in the request', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post' }); expect(req.headers.get('x-my-default-header')).toEqual('2'); }); - test('can ignore `undefined` and leave the default', () => { - const { req } = client.buildRequest({ + test('can ignore `undefined` and leave the default', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post', headers: { 'X-My-Default-Header': undefined }, @@ -40,8 +40,8 @@ describe('instantiate client', () => { expect(req.headers.get('x-my-default-header')).toEqual('2'); }); - test('can be removed with `null`', () => { - const { req } = client.buildRequest({ + test('can be removed with `null`', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post', headers: { 'X-My-Default-Header': null }, @@ -344,7 +344,7 @@ describe('instantiate client', () => { }); describe('withOptions', () => { - test('creates a new client with overridden options', () => { + test('creates a new client with overridden options', async () => { const client = new OpenAI({ baseURL: 'http://localhost:5000/', maxRetries: 3, apiKey: 'My API Key' }); const newClient = client.withOptions({ @@ -365,7 +365,7 @@ describe('instantiate client', () => { expect(newClient.constructor).toBe(client.constructor); }); - test('inherits options from the parent client', () => { + test('inherits options from the parent client', async () => { const client = new OpenAI({ baseURL: 'http://localhost:5000/', defaultHeaders: { 'X-Test-Header': 'test-value' }, @@ -380,7 +380,7 @@ describe('instantiate client', () => { // Test inherited options remain the same expect(newClient.buildURL('/foo', null)).toEqual('http://localhost:5001/foo?test-param=test-value'); - const { req } = newClient.buildRequest({ path: '/foo', method: 'get' }); + const { req } = await newClient.buildRequest({ path: '/foo', method: 'get' }); expect(req.headers.get('x-test-header')).toEqual('test-value'); }); @@ -430,8 +430,8 @@ describe('request building', () => { const client = new OpenAI({ apiKey: 'My API Key' }); describe('custom headers', () => { - test('handles undefined', () => { - const { req } = client.buildRequest({ + test('handles undefined', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post', body: { value: 'hello' }, @@ -466,8 +466,8 @@ describe('default encoder', () => { } } for (const jsonValue of [{}, [], { __proto__: null }, new Serializable(), new Collection(['item'])]) { - test(`serializes ${util.inspect(jsonValue)} as json`, () => { - const { req } = client.buildRequest({ + test(`serializes ${util.inspect(jsonValue)} as json`, async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post', body: jsonValue, @@ -490,7 +490,7 @@ describe('default encoder', () => { asyncIterable, ]) { test(`converts ${util.inspect(streamValue)} to ReadableStream`, async () => { - const { req } = client.buildRequest({ + const { req } = await client.buildRequest({ path: '/foo', method: 'post', body: streamValue, @@ -503,7 +503,7 @@ describe('default encoder', () => { } test(`can set content-type for ReadableStream`, async () => { - const { req } = client.buildRequest({ + const { req } = await client.buildRequest({ path: '/foo', method: 'post', body: new Response('a\nb\nc\n').body, diff --git a/tests/lib/azure.test.ts b/tests/lib/azure.test.ts index ef80e275b..49e3df1c0 100644 --- a/tests/lib/azure.test.ts +++ b/tests/lib/azure.test.ts @@ -30,13 +30,13 @@ describe('instantiate azure client', () => { apiVersion, }); - test('they are used in the request', () => { - const { req } = client.buildRequest({ path: '/foo', method: 'post' }); + test('they are used in the request', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post' }); expect(req.headers.get('x-my-default-header')).toEqual('2'); }); - test('can ignore `undefined` and leave the default', () => { - const { req } = client.buildRequest({ + test('can ignore `undefined` and leave the default', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post', headers: { 'X-My-Default-Header': undefined }, @@ -44,8 +44,8 @@ describe('instantiate azure client', () => { expect(req.headers.get('x-my-default-header')).toEqual('2'); }); - test('can be removed with `null`', () => { - const { req } = client.buildRequest({ + test('can be removed with `null`', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post', headers: { 'X-My-Default-Header': null }, @@ -53,8 +53,8 @@ describe('instantiate azure client', () => { expect(req.headers.has('x-my-default-header')).toBe(false); }); - test('includes retry count', () => { - const { req } = client.buildRequest( + test('includes retry count', async () => { + const { req } = await client.buildRequest( { path: '/foo', method: 'post', @@ -593,8 +593,8 @@ describe('azure request building', () => { }); describe('custom headers', () => { - test('handles undefined', () => { - const { req } = client.buildRequest({ + test('handles undefined', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post', body: { value: 'hello' }, diff --git a/tests/log.test.ts b/tests/log.test.ts index 1900e83b2..8971ee2e2 100644 --- a/tests/log.test.ts +++ b/tests/log.test.ts @@ -71,7 +71,7 @@ describe('debug()', () => { defaultHeaders: authorizationTest, }); - const { req } = client.buildRequest({ path: '/foo', method: 'post' }); + const { req } = await client.buildRequest({ path: '/foo', method: 'post' }); await client.post('/foo', {}); // Verify that the original headers weren't mutated @@ -100,7 +100,7 @@ describe('debug()', () => { fetch: opts.fetch, }); - const { req } = client.buildRequest({ path: '/foo', method: 'post' }); + const { req } = await client.buildRequest({ path: '/foo', method: 'post' }); await client.post('/foo', {}); // Verify that the original headers weren't mutated