Skip to content

Commit 625426c

Browse files
authored
Merge pull request #823 from openai/release-please--branches--master--changes--next--components--openai
2 parents 6c9cc82 + b10242a commit 625426c

File tree

10 files changed

+617
-35
lines changed

10 files changed

+617
-35
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "4.40.2"
2+
".": "4.41.0"
33
}

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 4.41.0 (2024-05-05)
4+
5+
Full Changelog: [v4.40.2...v4.41.0](https://github.com/openai/openai-node/compare/v4.40.2...v4.41.0)
6+
7+
### Features
8+
9+
* **client:** add Azure client ([#822](https://github.com/openai/openai-node/issues/822)) ([92f9049](https://github.com/openai/openai-node/commit/92f90499f0bbee79ba9c8342c8d58dbcaf88bdd1))
10+
311
## 4.40.2 (2024-05-03)
412

513
Full Changelog: [v4.40.1...v4.40.2](https://github.com/openai/openai-node/compare/v4.40.1...v4.40.2)

README.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ You can import in Deno via:
1919
<!-- x-release-please-start-version -->
2020

2121
```ts
22-
import OpenAI from 'https://deno.land/x/openai@v4.40.2/mod.ts';
22+
import OpenAI from 'https://deno.land/x/openai@v4.41.0/mod.ts';
2323
```
2424

2525
<!-- x-release-please-end -->
@@ -361,14 +361,25 @@ Error codes are as followed:
361361
| >=500 | `InternalServerError` |
362362
| N/A | `APIConnectionError` |
363363

364-
### Azure OpenAI
364+
## Microsoft Azure OpenAI
365365

366-
An example of using this library with Azure OpenAI can be found [here](https://github.com/openai/openai-node/blob/master/examples/azure.ts).
366+
To use this library with [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview), use the `AzureOpenAI`
367+
class instead of the `OpenAI` class.
367368

368-
Please note there are subtle differences in API shape & behavior between the Azure OpenAI API and the OpenAI API,
369-
so using this library with Azure OpenAI may result in incorrect types, which can lead to bugs.
369+
> [!IMPORTANT]
370+
> The Azure API shape differs from the core API shape which means that the static types for responses / params
371+
> won't always be correct.
372+
373+
```ts
374+
const openai = new AzureOpenAI();
370375

371-
See [`@azure/openai`](https://www.npmjs.com/package/@azure/openai) for an Azure-specific SDK provided by Microsoft.
376+
const result = await openai.chat.completions.create({
377+
model: 'gpt-4-1106-preview',
378+
messages: [{ role: 'user', content: 'Say hello!' }],
379+
});
380+
381+
console.log(result.choices[0]!.message?.content);
382+
```
372383

373384
### Retries
374385

examples/azure.ts

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,27 @@
11
#!/usr/bin/env -S npm run tsn -T
22

3-
import OpenAI from 'openai';
3+
import { AzureOpenAI } from 'openai';
44

5-
// The name of your Azure OpenAI Resource.
6-
// https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
7-
const resource = '<your resource name>';
8-
9-
// Corresponds to your Model deployment within your OpenAI resource, e.g. my-gpt35-16k-deployment
5+
// Corresponds to your Model deployment within your OpenAI resource, e.g. gpt-4-1106-preview
106
// Navigate to the Azure OpenAI Studio to deploy a model.
11-
const model = '<your model>';
12-
13-
// https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning
14-
const apiVersion = '2023-06-01-preview';
7+
const deployment = 'gpt-4-1106-preview';
158

16-
const apiKey = process.env['AZURE_OPENAI_API_KEY'];
17-
if (!apiKey) {
18-
throw new Error('The AZURE_OPENAI_API_KEY environment variable is missing or empty.');
19-
}
20-
21-
// Azure OpenAI requires a custom baseURL, api-version query param, and api-key header.
22-
const openai = new OpenAI({
23-
apiKey,
24-
baseURL: `https://${resource}.openai.azure.com/openai/deployments/${model}`,
25-
defaultQuery: { 'api-version': apiVersion },
26-
defaultHeaders: { 'api-key': apiKey },
27-
});
9+
// Make sure to set both AZURE_OPENAI_ENDPOINT with the endpoint of your Azure resource and AZURE_OPENAI_API_KEY with the API key.
10+
// You can find both information in the Azure Portal.
11+
const openai = new AzureOpenAI();
2812

2913
async function main() {
3014
console.log('Non-streaming:');
3115
const result = await openai.chat.completions.create({
32-
model,
16+
model: deployment,
3317
messages: [{ role: 'user', content: 'Say hello!' }],
3418
});
3519
console.log(result.choices[0]!.message?.content);
3620

3721
console.log();
3822
console.log('Streaming:');
3923
const stream = await openai.chat.completions.create({
40-
model,
24+
model: deployment,
4125
messages: [{ role: 'user', content: 'Say hello!' }],
4226
stream: true,
4327
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openai",
3-
"version": "4.40.2",
3+
"version": "4.41.0",
44
"description": "The official TypeScript library for the OpenAI API",
55
"author": "OpenAI <[email protected]>",
66
"types": "dist/index.d.ts",

scripts/build-deno

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This is a build produced from https://github.com/openai/openai-node – please g
1616
Usage:
1717
1818
\`\`\`ts
19-
import OpenAI from "https://deno.land/x/openai@v4.40.2/mod.ts";
19+
import OpenAI from "https://deno.land/x/openai@v4.41.0/mod.ts";
2020
2121
const client = new OpenAI();
2222
\`\`\`

src/index.ts

Lines changed: 184 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import * as Core from './core';
44
import * as Errors from './error';
5-
import { type Agent } from './_shims/index';
5+
import { type Agent, type RequestInit } from './_shims/index';
66
import * as Uploads from './uploads';
77
import * as Pagination from 'openai/pagination';
88
import * as API from 'openai/resources/index';
@@ -310,4 +310,187 @@ export namespace OpenAI {
310310
export import FunctionParameters = API.FunctionParameters;
311311
}
312312

313+
// ---------------------- Azure ----------------------
314+
315+
/** API Client for interfacing with the Azure OpenAI API. */
316+
export interface AzureClientOptions extends ClientOptions {
317+
/**
318+
* Defaults to process.env['OPENAI_API_VERSION'].
319+
*/
320+
apiVersion?: string | undefined;
321+
322+
/**
323+
* Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/`
324+
*/
325+
endpoint?: string | undefined;
326+
327+
/**
328+
* A model deployment, if given, sets the base client URL to include `/deployments/{deployment}`.
329+
* Note: this means you won't be able to use non-deployment endpoints. Not supported with Assistants APIs.
330+
*/
331+
deployment?: string | undefined;
332+
333+
/**
334+
* Defaults to process.env['AZURE_OPENAI_API_KEY'].
335+
*/
336+
apiKey?: string | undefined;
337+
338+
/**
339+
* A function that returns an access token for Microsoft Entra (formerly known as Azure Active Directory),
340+
* which will be invoked on every request.
341+
*/
342+
azureADTokenProvider?: (() => string) | undefined;
343+
}
344+
345+
/** API Client for interfacing with the Azure OpenAI API. */
346+
export class AzureOpenAI extends OpenAI {
347+
private _azureADTokenProvider: (() => string) | undefined;
348+
apiVersion: string = '';
349+
/**
350+
* API Client for interfacing with the Azure OpenAI API.
351+
*
352+
* @param {string | undefined} [opts.apiVersion=process.env['OPENAI_API_VERSION'] ?? undefined]
353+
* @param {string | undefined} [opts.endpoint=process.env['AZURE_OPENAI_ENDPOINT'] ?? undefined] - Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/`
354+
* @param {string | undefined} [opts.apiKey=process.env['AZURE_OPENAI_API_KEY'] ?? undefined]
355+
* @param {string | undefined} opts.deployment - A model deployment, if given, sets the base client URL to include `/deployments/{deployment}`.
356+
* @param {string | null | undefined} [opts.organization=process.env['OPENAI_ORG_ID'] ?? null]
357+
* @param {string} [opts.baseURL=process.env['OPENAI_BASE_URL']] - Sets the base URL for the API.
358+
* @param {number} [opts.timeout=10 minutes] - The maximum amount of time (in milliseconds) the client will wait for a response before timing out.
359+
* @param {number} [opts.httpAgent] - An HTTP agent used to manage HTTP(s) connections.
360+
* @param {Core.Fetch} [opts.fetch] - Specify a custom `fetch` function implementation.
361+
* @param {number} [opts.maxRetries=2] - The maximum number of times the client will retry a request.
362+
* @param {Core.Headers} opts.defaultHeaders - Default headers to include with every request to the API.
363+
* @param {Core.DefaultQuery} opts.defaultQuery - Default query parameters to include with every request to the API.
364+
* @param {boolean} [opts.dangerouslyAllowBrowser=false] - By default, client-side use of this library is not allowed, as it risks exposing your secret API credentials to attackers.
365+
*/
366+
constructor({
367+
baseURL = Core.readEnv('OPENAI_BASE_URL'),
368+
apiKey = Core.readEnv('AZURE_OPENAI_API_KEY'),
369+
apiVersion = Core.readEnv('OPENAI_API_VERSION'),
370+
endpoint,
371+
deployment,
372+
azureADTokenProvider,
373+
dangerouslyAllowBrowser,
374+
...opts
375+
}: AzureClientOptions = {}) {
376+
if (!apiVersion) {
377+
throw new Errors.OpenAIError(
378+
"The OPENAI_API_VERSION environment variable is missing or empty; either provide it, or instantiate the AzureOpenAI client with an apiVersion option, like new AzureOpenAI({ apiVersion: 'My API Version' }).",
379+
);
380+
}
381+
382+
if (typeof azureADTokenProvider === 'function') {
383+
dangerouslyAllowBrowser = true;
384+
}
385+
386+
if (!azureADTokenProvider && !apiKey) {
387+
throw new Errors.OpenAIError(
388+
'Missing credentials. Please pass one of `apiKey` and `azureADTokenProvider`, or set the `AZURE_OPENAI_API_KEY` environment variable.',
389+
);
390+
}
391+
392+
if (azureADTokenProvider && apiKey) {
393+
throw new Errors.OpenAIError(
394+
'The `apiKey` and `azureADTokenProvider` arguments are mutually exclusive; only one can be passed at a time.',
395+
);
396+
}
397+
398+
// define a sentinel value to avoid any typing issues
399+
apiKey ??= API_KEY_SENTINEL;
400+
401+
opts.defaultQuery = { ...opts.defaultQuery, 'api-version': apiVersion };
402+
403+
if (!baseURL) {
404+
if (!endpoint) {
405+
endpoint = process.env['AZURE_OPENAI_ENDPOINT'];
406+
}
407+
408+
if (!endpoint) {
409+
throw new Errors.OpenAIError(
410+
'Must provide one of the `baseURL` or `endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable',
411+
);
412+
}
413+
414+
if (deployment) {
415+
baseURL = `${endpoint}/openai/deployments/${deployment}`;
416+
} else {
417+
baseURL = `${endpoint}/openai`;
418+
}
419+
} else {
420+
if (endpoint) {
421+
throw new Errors.OpenAIError('baseURL and endpoint are mutually exclusive');
422+
}
423+
}
424+
425+
super({
426+
apiKey,
427+
baseURL,
428+
...opts,
429+
...(dangerouslyAllowBrowser !== undefined ? { dangerouslyAllowBrowser } : {}),
430+
});
431+
432+
this._azureADTokenProvider = azureADTokenProvider;
433+
this.apiVersion = apiVersion;
434+
}
435+
436+
override buildRequest(options: Core.FinalRequestOptions<unknown>): {
437+
req: RequestInit;
438+
url: string;
439+
timeout: number;
440+
} {
441+
if (_deployments_endpoints.has(options.path) && options.method === 'post' && options.body !== undefined) {
442+
if (!Core.isObj(options.body)) {
443+
throw new Error('Expected request body to be an object');
444+
}
445+
const model = options.body['model'];
446+
delete options.body['model'];
447+
if (model !== undefined && !this.baseURL.includes('/deployments')) {
448+
options.path = `/deployments/${model}${options.path}`;
449+
}
450+
}
451+
return super.buildRequest(options);
452+
}
453+
454+
private _getAzureADToken(): string | undefined {
455+
if (typeof this._azureADTokenProvider === 'function') {
456+
const token = this._azureADTokenProvider();
457+
if (!token || typeof token !== 'string') {
458+
throw new Errors.OpenAIError(
459+
`Expected 'azureADTokenProvider' argument to return a string but it returned ${token}`,
460+
);
461+
}
462+
return token;
463+
}
464+
return undefined;
465+
}
466+
467+
protected override authHeaders(opts: Core.FinalRequestOptions): Core.Headers {
468+
if (opts.headers?.['Authorization'] || opts.headers?.['api-key']) {
469+
return {};
470+
}
471+
const token = this._getAzureADToken();
472+
if (token) {
473+
return { Authorization: `Bearer ${token}` };
474+
}
475+
if (this.apiKey !== API_KEY_SENTINEL) {
476+
return { 'api-key': this.apiKey };
477+
}
478+
throw new Errors.OpenAIError('Unable to handle auth');
479+
}
480+
}
481+
482+
const _deployments_endpoints = new Set([
483+
'/completions',
484+
'/chat/completions',
485+
'/embeddings',
486+
'/audio/transcriptions',
487+
'/audio/translations',
488+
'/audio/speech',
489+
'/images/generations',
490+
]);
491+
492+
const API_KEY_SENTINEL = '<Missing Key>';
493+
494+
// ---------------------- End Azure ----------------------
495+
313496
export default OpenAI;

src/resources/beta/vector-stores/files.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ export class Files extends APIResource {
144144

145145
/**
146146
* Upload a file to the `files` API and then attach it to the given vector store.
147+
*
147148
* Note the file will be asynchronously processed (you can use the alternative
148149
* polling helper method to wait for processing to complete).
149150
*/

src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const VERSION = '4.40.2'; // x-release-please-version
1+
export const VERSION = '4.41.0'; // x-release-please-version

0 commit comments

Comments
 (0)