diff --git a/api.md b/api.md
index a5ee1a25b..b19532071 100644
--- a/api.md
+++ b/api.md
@@ -2,12 +2,12 @@
Types:
-- Completion
-- CompletionChoice
+- Completion
+- CompletionChoice
Methods:
-- client.completions.create({ ...params }) -> Completion
+- client.completions.create({ ...params }) -> Completion
# Chat
@@ -15,61 +15,61 @@ Methods:
Types:
-- ChatCompletion
-- ChatCompletionChunk
+- ChatCompletion
+- ChatCompletionChunk
Methods:
-- client.chat.completions.create({ ...params }) -> ChatCompletion
+- client.chat.completions.create({ ...params }) -> ChatCompletion
# Edits
Types:
-- Edit
+- Edit
Methods:
-- client.edits.create({ ...params }) -> Edit
+- client.edits.create({ ...params }) -> Edit
# Embeddings
Types:
-- Embedding
+- Embedding
Methods:
-- client.embeddings.create({ ...params }) -> Embedding
+- client.embeddings.create({ ...params }) -> Embedding
# Files
Types:
-- FileContent
-- FileDeleted
-- FileObject
+- FileContent
+- FileDeleted
+- FileObject
Methods:
-- client.files.create({ ...params }) -> FileObject
-- client.files.retrieve(fileId) -> FileObject
-- client.files.list() -> FileObjectsPage
-- client.files.del(fileId) -> FileDeleted
-- client.files.retrieveFileContent(fileId) -> string
+- client.files.create({ ...params }) -> FileObject
+- client.files.retrieve(fileId) -> FileObject
+- client.files.list() -> FileObjectsPage
+- client.files.del(fileId) -> FileDeleted
+- client.files.retrieveFileContent(fileId) -> string
# Images
Types:
-- Image
-- ImagesResponse
+- Image
+- ImagesResponse
Methods:
-- client.images.createVariation({ ...params }) -> ImagesResponse
-- client.images.edit({ ...params }) -> ImagesResponse
-- client.images.generate({ ...params }) -> ImagesResponse
+- client.images.createVariation({ ...params }) -> ImagesResponse
+- client.images.edit({ ...params }) -> ImagesResponse
+- client.images.generate({ ...params }) -> ImagesResponse
# Audio
@@ -77,58 +77,58 @@ Methods:
Types:
-- Transcription
+- Transcription
Methods:
-- client.audio.transcriptions.create({ ...params }) -> Transcription
+- client.audio.transcriptions.create({ ...params }) -> Transcription
## Translations
Types:
-- Translation
+- Translation
Methods:
-- client.audio.translations.create({ ...params }) -> Translation
+- client.audio.translations.create({ ...params }) -> Translation
# Moderations
Types:
-- Moderation
-- ModerationCreateResponse
+- Moderation
+- ModerationCreateResponse
Methods:
-- client.moderations.create({ ...params }) -> ModerationCreateResponse
+- client.moderations.create({ ...params }) -> ModerationCreateResponse
# Models
Types:
-- Model
-- ModelDeleted
+- Model
+- ModelDeleted
Methods:
-- client.models.retrieve(model) -> Model
-- client.models.list() -> ModelsPage
-- client.models.del(model) -> ModelDeleted
+- client.models.retrieve(model) -> Model
+- client.models.list() -> ModelsPage
+- client.models.del(model) -> ModelDeleted
# FineTunes
Types:
-- FineTune
-- FineTuneEvent
-- FineTuneEventsListResponse
+- FineTune
+- FineTuneEvent
+- FineTuneEventsListResponse
Methods:
-- client.fineTunes.create({ ...params }) -> FineTune
-- client.fineTunes.retrieve(fineTuneId) -> FineTune
-- client.fineTunes.list() -> FineTunesPage
-- client.fineTunes.cancel(fineTuneId) -> FineTune
-- client.fineTunes.listEvents(fineTuneId, { ...params }) -> FineTuneEventsListResponse
+- client.fineTunes.create({ ...params }) -> FineTune
+- client.fineTunes.retrieve(fineTuneId) -> FineTune
+- client.fineTunes.list() -> FineTunesPage
+- client.fineTunes.cancel(fineTuneId) -> FineTune
+- client.fineTunes.listEvents(fineTuneId, { ...params }) -> FineTuneEventsListResponse
diff --git a/build b/build
index 4370f6ba7..b205edf8d 100755
--- a/build
+++ b/build
@@ -3,6 +3,8 @@ set -exuo pipefail
node scripts/check-version.cjs
+yarn tsc
+
# Build into dist and will publish the package from there,
# so that src/resources/foo.ts becomes /resources/foo.js
# This way importing from `"openai/resources/foo"` works
@@ -12,7 +14,10 @@ rm -rf dist
mkdir dist
# Copy src to dist/src and build from dist/src into dist, so that
# the source map for index.js.map will refer to ./src/index.ts etc
-cp -rp src dist
+cp -rp src README.md dist
+for file in LICENSE CHANGELOG.md; do
+ if [ -e "${file}" ]; then cp "${file}" dist; fi
+done
# this converts the export map paths for the dist directory
# and does a few other minor things
node scripts/make-dist-package-json.cjs > dist/package.json
@@ -21,7 +26,7 @@ node scripts/make-dist-package-json.cjs > dist/package.json
tsc-multi
# copy over handwritten .js/.mjs/.d.ts files
cp src/_shims/*.{d.ts,js,mjs} dist/_shims
-tsc-alias -p tsconfig.json --resolve-full-paths
+tsc-alias -p tsconfig.build.json
# we need to add exports = module.exports = OpenAI Node to index.js;
# No way to get that from index.ts because it would cause compile errors
# when building .mjs
diff --git a/ecosystem-tests/vercel-edge/package-lock.json b/ecosystem-tests/vercel-edge/package-lock.json
index 223fbc94f..935112892 100644
--- a/ecosystem-tests/vercel-edge/package-lock.json
+++ b/ecosystem-tests/vercel-edge/package-lock.json
@@ -14,7 +14,7 @@
"react-dom": "18.2.0"
},
"devDependencies": {
- "@types/node": "20.3.1",
+ "@types/node": "20.3.3",
"@types/react": "18.2.13",
"@types/react-dom": "18.2.6",
"edge-runtime": "^2.4.3",
@@ -3095,9 +3095,9 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "20.3.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
- "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==",
+ "version": "20.3.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz",
+ "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==",
"dev": true
},
"node_modules/@types/node-fetch": {
@@ -13739,9 +13739,9 @@
"dev": true
},
"@types/node": {
- "version": "20.3.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
- "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==",
+ "version": "20.3.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz",
+ "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==",
"dev": true
},
"@types/node-fetch": {
diff --git a/ecosystem-tests/vercel-edge/package.json b/ecosystem-tests/vercel-edge/package.json
index 14ee1da4f..b44940e0e 100644
--- a/ecosystem-tests/vercel-edge/package.json
+++ b/ecosystem-tests/vercel-edge/package.json
@@ -17,7 +17,7 @@
"react-dom": "18.2.0"
},
"devDependencies": {
- "@types/node": "20.3.1",
+ "@types/node": "20.3.3",
"@types/react": "18.2.13",
"@types/react-dom": "18.2.6",
"edge-runtime": "^2.4.3",
diff --git a/examples/azure.ts b/examples/azure.ts
new file mode 100644
index 000000000..0076d101d
--- /dev/null
+++ b/examples/azure.ts
@@ -0,0 +1,48 @@
+#!/usr/bin/env yarn tsn -T
+
+import OpenAI from 'openai';
+
+// The name of your Azure OpenAI Resource.
+// https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
+const resource = '';
+
+// Corresponds to your Model deployment within your OpenAI resource, e.g. my-gpt35-16k-deployment
+// Navigate to the Azure OpenAI Studio to deploy a model.
+const model = '';
+
+const apiKey = process.env['AZURE_OPENAI_API_KEY'];
+if (!apiKey) {
+ throw new Error('The AZURE_OPENAI_API_KEY environment variable is missing or empty.');
+}
+
+// Azure OpenAI requires a custom baseURL, api-version query param, and api-key header.
+const openai = new OpenAI({
+ apiKey,
+ baseURL: `https://${resource}.openai.azure.com/openai/deployments/${model}`,
+ defaultQuery: { 'api-version': '2023-06-01-preview' },
+ defaultHeaders: { 'api-key': apiKey },
+});
+
+async function main() {
+ console.log('Non-streaming:');
+ const result = await openai.chat.completions.create({
+ model,
+ messages: [{ role: 'user', content: 'Say hello!' }],
+ });
+ console.log(result.choices[0]!.message?.content);
+
+ console.log();
+ console.log('Streaming:');
+ const stream = await openai.chat.completions.create({
+ model,
+ messages: [{ role: 'user', content: 'Say hello!' }],
+ stream: true,
+ });
+
+ for await (const part of stream) {
+ process.stdout.write(part.choices[0]?.delta?.content ?? '');
+ }
+ process.stdout.write('\n');
+}
+
+main().catch(console.error);
diff --git a/package.json b/package.json
index dc852bda0..43e32027b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "openai",
- "version": "4.0.0-beta.2",
+ "version": "4.0.0-beta.3",
"description": "Client library for the OpenAI API",
"author": "OpenAI ",
"types": "dist/index.d.ts",
diff --git a/scripts/replace-self-referencing-imports.js b/scripts/replace-self-referencing-imports.js
new file mode 100644
index 000000000..de3246dd3
--- /dev/null
+++ b/scripts/replace-self-referencing-imports.js
@@ -0,0 +1,21 @@
+'use strict';
+Object.defineProperty(exports, '__esModule', { value: true });
+
+const path = require('path');
+const distSrcDir = path.resolve(__dirname, '..', 'dist', 'src');
+
+function replaceSelfReferencingImports({ orig, file, config }) {
+ // replace self-referencing imports in source files to reduce errors users will
+ // see if they go to definition
+ if (!file.startsWith(distSrcDir)) return orig;
+ return orig.replace(/['"]([^"'\r\n]+)['"]/, (match, importPath) => {
+ if (!importPath.startsWith('openai/')) return match;
+ let relativePath = path.relative(
+ path.dirname(file),
+ path.join(distSrcDir, importPath.substring('openai/'.length)),
+ );
+ if (!relativePath.startsWith('.')) relativePath = `./${relativePath}`;
+ return JSON.stringify(relativePath);
+ });
+}
+exports.default = replaceSelfReferencingImports;
diff --git a/src/core.ts b/src/core.ts
index 22f436ac8..50c7734a5 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -73,6 +73,8 @@ export abstract class APIClient {
};
}
+ protected abstract defaultQuery(): DefaultQuery | undefined;
+
/**
* Override this to add your own headers validation:
*/
@@ -130,9 +132,17 @@ export abstract class APIClient {
const contentLength = typeof body === 'string' ? body.length.toString() : null;
const url = this.buildURL(path!, query);
- const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url);
+ if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
const timeout = options.timeout ?? this.timeout;
- validatePositiveInteger('timeout', timeout);
+ const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url);
+ const minAgentTimeout = timeout + 1000;
+ if ((httpAgent as any)?.options && minAgentTimeout > ((httpAgent as any).options.timeout ?? 0)) {
+ // Allow any given request to bump our agent active socket timeout.
+ // This may seem strange, but leaking active sockets should be rare and not particularly problematic,
+ // and without mutating agent we would need to create more of them.
+ // This tradeoff optimizes for performance.
+ (httpAgent as any).options.timeout = minAgentTimeout;
+ }
if (this.idempotencyHeader && method !== 'get') {
if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey();
@@ -260,6 +270,11 @@ export abstract class APIClient {
new URL(path)
: new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
+ const defaultQuery = this.defaultQuery();
+ if (!isEmptyObj(defaultQuery)) {
+ query = { ...defaultQuery, ...query } as Req;
+ }
+
if (query) {
url.search = qs.stringify(query, this.qsOptions());
}
@@ -516,6 +531,7 @@ type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
export type RequestClient = { fetch: Fetch };
export type Headers = Record;
+export type DefaultQuery = Record;
export type KeysEnum = { [P in keyof Required]: true };
export type RequestOptions | Readable> = {
@@ -564,6 +580,7 @@ export type FinalRequestOptions | Reada
};
export type APIResponse = T & {
+ /** @deprecated - we plan to add a different way to access raw response information shortly. */
responseHeaders: Headers;
};
diff --git a/src/index.ts b/src/index.ts
index f960020dd..87063761e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -13,11 +13,52 @@ type Config = {
* Defaults to process.env["OPENAI_API_KEY"].
*/
apiKey?: string;
+
+ /**
+ * Override the default base URL for the API, e.g., "https://api.example.com/v2/"
+ */
baseURL?: string;
+
+ /**
+ * The maximum amount of time (in milliseconds) that the client should wait for a response
+ * from the server before timing out a single request.
+ *
+ * Note that request timeouts are retried by default, so in a worst-case scenario you may wait
+ * much longer than this timeout before the promise succeeds or fails.
+ */
timeout?: number;
+
+ /**
+ * An HTTP agent used to manage HTTP(S) connections.
+ *
+ * If not provided, an agent will be constructed by default in the Node.js environment,
+ * otherwise no agent is used.
+ */
httpAgent?: Agent;
+
+ /**
+ * The maximum number of times that the client will retry a request in case of a
+ * temporary failure, like a network error or a 5XX error from the server.
+ *
+ * @default 2
+ */
maxRetries?: number;
+
+ /**
+ * Default headers to include with every request to the API.
+ *
+ * These can be removed in individual requests by explicitly setting the
+ * header to `undefined` or `null` in request options.
+ */
defaultHeaders?: Core.Headers;
+
+ /**
+ * Default query parameters to include with every request to the API.
+ *
+ * These can be removed in individual requests by explicitly setting the
+ * param to `undefined` in request options.
+ */
+ defaultQuery?: Core.DefaultQuery;
};
/** Instantiate the API Client. */
@@ -41,7 +82,7 @@ export class OpenAI extends Core.APIClient {
super({
baseURL: options.baseURL!,
- timeout: options.timeout,
+ timeout: options.timeout ?? 600000,
httpAgent: options.httpAgent,
maxRetries: options.maxRetries,
});
@@ -60,6 +101,10 @@ export class OpenAI extends Core.APIClient {
models: API.Models = new API.Models(this);
fineTunes: API.FineTunes = new API.FineTunes(this);
+ protected override defaultQuery(): Core.DefaultQuery | undefined {
+ return this._options.defaultQuery;
+ }
+
protected override defaultHeaders(): Core.Headers {
return {
...super.defaultHeaders(),
diff --git a/src/resources/chat/completions.ts b/src/resources/chat/completions.ts
index 1e78dd230..ffc94228b 100644
--- a/src/resources/chat/completions.ts
+++ b/src/resources/chat/completions.ts
@@ -229,7 +229,7 @@ export namespace CompletionCreateParams {
* increase likelihood of selection; values like -100 or 100 should result in a ban
* or exclusive selection of the relevant token.
*/
- logit_bias?: unknown | null;
+ logit_bias?: Record | null;
/**
* The maximum number of [tokens](/tokenizer) to generate in the chat completion.
@@ -451,7 +451,7 @@ export namespace CompletionCreateParams {
* increase likelihood of selection; values like -100 or 100 should result in a ban
* or exclusive selection of the relevant token.
*/
- logit_bias?: unknown | null;
+ logit_bias?: Record | null;
/**
* The maximum number of [tokens](/tokenizer) to generate in the chat completion.
diff --git a/src/resources/completions.ts b/src/resources/completions.ts
index 7f93a423f..f123db24d 100644
--- a/src/resources/completions.ts
+++ b/src/resources/completions.ts
@@ -67,7 +67,7 @@ export namespace CompletionChoice {
tokens?: Array;
- top_logprobs?: Array;
+ top_logprobs?: Array>;
}
}
@@ -145,7 +145,7 @@ export namespace CompletionCreateParams {
* As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
* from being generated.
*/
- logit_bias?: unknown | null;
+ logit_bias?: Record | null;
/**
* Include the log probabilities on the `logprobs` most likely tokens, as well the
@@ -310,7 +310,7 @@ export namespace CompletionCreateParams {
* As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
* from being generated.
*/
- logit_bias?: unknown | null;
+ logit_bias?: Record | null;
/**
* Include the log probabilities on the `logprobs` most likely tokens, as well the
diff --git a/src/resources/edits.ts b/src/resources/edits.ts
index 66ca51347..5e9e44510 100644
--- a/src/resources/edits.ts
+++ b/src/resources/edits.ts
@@ -7,6 +7,10 @@ import * as API from './';
export class Edits extends APIResource {
/**
* Creates a new edit for the provided input, instruction, and parameters.
+ *
+ * @deprecated The Edits API is deprecated; please use Chat Completions instead.
+ *
+ * https://openai.com/blog/gpt-4-api-general-availability#deprecation-of-the-edits-api
*/
create(body: EditCreateParams, options?: Core.RequestOptions): Promise> {
return this.post('/edits', { body, ...options });
@@ -42,7 +46,7 @@ export namespace Edit {
tokens?: Array;
- top_logprobs?: Array;
+ top_logprobs?: Array>;
}
}
diff --git a/src/resources/files.ts b/src/resources/files.ts
index 53e01ca6c..03fe57de1 100644
--- a/src/resources/files.ts
+++ b/src/resources/files.ts
@@ -53,6 +53,8 @@ export class Files extends APIResource {
* Note: no pagination actually occurs yet, this is for forwards-compatibility.
*/
export class FileObjectsPage extends Page {}
+// alias so we can export it in the namespace
+type _FileObjectsPage = FileObjectsPage;
export type FileContent = string;
@@ -79,7 +81,7 @@ export interface FileObject {
status?: string;
- status_details?: unknown | null;
+ status_details?: string | null;
}
export interface FileCreateParams {
@@ -106,6 +108,6 @@ export namespace Files {
export import FileContent = API.FileContent;
export import FileDeleted = API.FileDeleted;
export import FileObject = API.FileObject;
- export import FileObjectsPage = API.FileObjectsPage;
+ export type FileObjectsPage = _FileObjectsPage;
export import FileCreateParams = API.FileCreateParams;
}
diff --git a/src/resources/fine-tunes.ts b/src/resources/fine-tunes.ts
index 551674016..44e71888b 100644
--- a/src/resources/fine-tunes.ts
+++ b/src/resources/fine-tunes.ts
@@ -63,6 +63,7 @@ export class FineTunes extends APIResource {
): Promise>> {
return this.get(`/fine-tunes/${fineTuneId}/events`, {
query,
+ timeout: 86400000,
...options,
stream: query?.stream ?? false,
});
@@ -73,6 +74,8 @@ export class FineTunes extends APIResource {
* Note: no pagination actually occurs yet, this is for forwards-compatibility.
*/
export class FineTunesPage extends Page {}
+// alias so we can export it in the namespace
+type _FineTunesPage = FineTunesPage;
export interface FineTune {
id: string;
@@ -81,7 +84,7 @@ export interface FineTune {
fine_tuned_model: string | null;
- hyperparams: unknown;
+ hyperparams: FineTune.Hyperparams;
model: string;
@@ -102,6 +105,24 @@ export interface FineTune {
events?: Array;
}
+export namespace FineTune {
+ export interface Hyperparams {
+ batch_size: number;
+
+ learning_rate_multiplier: number;
+
+ n_epochs: number;
+
+ prompt_loss_weight: number;
+
+ classification_n_classes?: number;
+
+ classification_positive_class?: string;
+
+ compute_classification_metrics?: boolean;
+ }
+}
+
export interface FineTuneEvent {
created_at: number;
@@ -281,7 +302,7 @@ export namespace FineTunes {
export import FineTune = API.FineTune;
export import FineTuneEvent = API.FineTuneEvent;
export import FineTuneEventsListResponse = API.FineTuneEventsListResponse;
- export import FineTunesPage = API.FineTunesPage;
+ export type FineTunesPage = _FineTunesPage;
export import FineTuneCreateParams = API.FineTuneCreateParams;
export import FineTuneListEventsParams = API.FineTuneListEventsParams;
}
diff --git a/src/resources/models.ts b/src/resources/models.ts
index 08a25ad9d..2b6ef9ad9 100644
--- a/src/resources/models.ts
+++ b/src/resources/models.ts
@@ -34,6 +34,8 @@ export class Models extends APIResource {
* Note: no pagination actually occurs yet, this is for forwards-compatibility.
*/
export class ModelsPage extends Page {}
+// alias so we can export it in the namespace
+type _ModelsPage = ModelsPage;
export interface Model {
id: string;
@@ -56,5 +58,5 @@ export interface ModelDeleted {
export namespace Models {
export import Model = API.Model;
export import ModelDeleted = API.ModelDeleted;
- export import ModelsPage = API.ModelsPage;
+ export type ModelsPage = _ModelsPage;
}
diff --git a/src/streaming.ts b/src/streaming.ts
index 3de8e2ba2..46e93bc3c 100644
--- a/src/streaming.ts
+++ b/src/streaming.ts
@@ -1,69 +1,19 @@
import type { Response } from 'openai/_shims/fetch';
+
import { APIResponse, Headers, createResponseHeaders } from './core';
+type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined;
+
type ServerSentEvent = {
event: string | null;
data: string;
raw: string[];
};
-class SSEDecoder {
- private data: string[];
- private event: string | null;
- private chunks: string[];
-
- constructor() {
- this.event = null;
- this.data = [];
- this.chunks = [];
- }
-
- decode(line: string) {
- if (line.endsWith('\r')) {
- line = line.substring(0, line.length - 1);
- }
-
- if (!line) {
- // empty line and we didn't previously encounter any messages
- if (!this.event && !this.data.length) return null;
-
- const sse: ServerSentEvent = {
- event: this.event,
- data: this.data.join('\n'),
- raw: this.chunks,
- };
-
- this.event = null;
- this.data = [];
- this.chunks = [];
-
- return sse;
- }
-
- this.chunks.push(line);
-
- if (line.startsWith(':')) {
- return null;
- }
-
- let [fieldname, _, value] = partition(line, ':');
-
- if (value.startsWith(' ')) {
- value = value.substring(1);
- }
-
- if (fieldname === 'event') {
- this.event = value;
- } else if (fieldname === 'data') {
- this.data.push(value);
- }
-
- return null;
- }
-}
-
export class Stream- implements AsyncIterable
- , APIResponse> {
+ /** @deprecated - please use the async iterator instead. We plan to add additional helper methods shortly. */
response: Response;
+ /** @deprecated - we plan to add a different way to access raw response information shortly. */
responseHeaders: Headers;
controller: AbortController;
@@ -81,21 +31,11 @@ export class Stream
- implements AsyncIterable
- , APIResponse(this.response.body);
+ for await (const chunk of iter) {
+ for (const line of lineDecoder.decode(chunk)) {
const sse = this.decoder.decode(line);
if (sse) yield sse;
}
@@ -135,7 +75,60 @@ export class Stream
- implements AsyncIterable
- , APIResponse(stream: any): AsyncIterableIterator {
+ if (stream[Symbol.asyncIterator]) return stream[Symbol.asyncIterator];
+
+ const reader = stream.getReader();
+ return {
+ next() {
+ return reader.read();
+ },
+ async return() {
+ reader.cancel();
+ reader.releaseLock();
+ return { done: true, value: undefined };
+ },
+ [Symbol.asyncIterator]() {
+ return this;
+ },
+ };
+}
diff --git a/src/uploads.ts b/src/uploads.ts
index 26cbcbab7..2b19b31da 100644
--- a/src/uploads.ts
+++ b/src/uploads.ts
@@ -96,7 +96,7 @@ export type ToFileInput = Uploadable | Exclude | AsyncIterable
* @returns a {@link File} with the given properties
*/
export async function toFile(
- value: ToFileInput,
+ value: ToFileInput | PromiseLike,
name?: string | null | undefined,
options: FilePropertyBag | undefined = {},
): Promise {
@@ -121,8 +121,9 @@ export async function toFile(
return new File(bits, name, options);
}
-async function getBytes(value: ToFileInput): Promise> {
- if (value instanceof Promise) return getBytes(await (value as any));
+async function getBytes(value: ToFileInput | PromiseLike): Promise> {
+ // resolve input promise or promiselike object
+ value = await value;
let parts: Array = [];
if (
diff --git a/src/version.ts b/src/version.ts
index 4e42addf0..6c18f5dc0 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '4.0.0-beta.2';
+export const VERSION = '4.0.0-beta.3';
diff --git a/tests/api-resources/chat/completions.test.ts b/tests/api-resources/chat/completions.test.ts
index 96076cf1a..bdf82568e 100644
--- a/tests/api-resources/chat/completions.test.ts
+++ b/tests/api-resources/chat/completions.test.ts
@@ -26,7 +26,7 @@ describe('resource completions', () => {
frequency_penalty: -2,
function_call: 'none',
functions: [{ name: 'string', description: 'string', parameters: { foo: 'bar' } }],
- logit_bias: {},
+ logit_bias: { foo: 0 },
max_tokens: 0,
n: 1,
presence_penalty: -2,
diff --git a/tests/api-resources/completions.test.ts b/tests/api-resources/completions.test.ts
index 6d6ac8347..0067223fb 100644
--- a/tests/api-resources/completions.test.ts
+++ b/tests/api-resources/completions.test.ts
@@ -16,7 +16,7 @@ describe('resource completions', () => {
best_of: 0,
echo: true,
frequency_penalty: -2,
- logit_bias: {},
+ logit_bias: { foo: 0 },
logprobs: 0,
max_tokens: 16,
n: 1,
diff --git a/tests/index.test.ts b/tests/index.test.ts
index cf112ea61..30b0f80b3 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -17,11 +17,64 @@ describe('instantiate client', () => {
process.env = env;
});
- test('defaultHeaders are passed through', () => {
- const client = new OpenAI({ defaultHeaders: { 'X-My-Default-Header': '2' }, apiKey: 'my api key' });
+ describe('defaultHeaders', () => {
+ const client = new OpenAI({
+ baseURL: 'http://localhost:5000/',
+ defaultHeaders: { 'X-My-Default-Header': '2' },
+ apiKey: 'my api key',
+ });
+
+ test('they are used in the request', () => {
+ const { req } = client.buildRequest({ path: '/foo', method: 'post' });
+ expect((req.headers as Headers)['X-My-Default-Header']).toEqual('2');
+ });
- const { req } = client.buildRequest({ path: '/foo', method: 'post' });
- expect((req.headers as Headers)['X-My-Default-Header']).toEqual('2');
+ test('can be overriden with `undefined`', () => {
+ const { req } = client.buildRequest({
+ path: '/foo',
+ method: 'post',
+ headers: { 'X-My-Default-Header': undefined },
+ });
+ expect((req.headers as Headers)['X-My-Default-Header']).toBeUndefined();
+ });
+
+ test('can be overriden with `null`', () => {
+ const { req } = client.buildRequest({
+ path: '/foo',
+ method: 'post',
+ headers: { 'X-My-Default-Header': null },
+ });
+ expect((req.headers as Headers)['X-My-Default-Header']).toBeUndefined();
+ });
+ });
+
+ describe('defaultQuery', () => {
+ test('with null query params given', () => {
+ const client = new OpenAI({
+ baseURL: 'http://localhost:5000/',
+ defaultQuery: { apiVersion: 'foo' },
+ apiKey: 'my api key',
+ });
+ expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo');
+ });
+
+ test('multiple default query params', () => {
+ const client = new OpenAI({
+ baseURL: 'http://localhost:5000/',
+ defaultQuery: { apiVersion: 'foo', hello: 'world' },
+ apiKey: 'my api key',
+ });
+ expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo&hello=world');
+ });
+
+ test('overriding with `undefined`', () => {
+ const client = new OpenAI({
+ baseURL: 'http://localhost:5000/',
+ defaultQuery: { hello: 'world' },
+ apiKey: 'my api key',
+ });
+ expect(client.buildURL('/foo', { hello: undefined })).toEqual('http://localhost:5000/foo');
+ });
});
describe('baseUrl', () => {
diff --git a/tsconfig.build.json b/tsconfig.build.json
index ca9e53425..c3660e68f 100644
--- a/tsconfig.build.json
+++ b/tsconfig.build.json
@@ -1,43 +1,32 @@
{
+ "extends": "./tsconfig.json",
"include": ["dist/src"],
"exclude": [],
"compilerOptions": {
- "target": "es2019",
- "lib": ["es2020"],
- "module": "commonjs",
- "moduleResolution": "node",
- "esModuleInterop": true,
"rootDir": "./dist/src",
- "baseUrl": "./",
"paths": {
"openai/_shims/*": ["dist/src/_shims/*.node"],
"openai": ["dist/src/index.ts"],
"openai/*": ["dist/src/*"],
"digest-fetch": ["./typings/digest-fetch"]
},
-
+ "noEmit": false,
"declaration": true,
"declarationMap": true,
"outDir": "dist",
"pretty": true,
- "sourceMap": true,
- "resolveJsonModule": true,
-
- "forceConsistentCasingInFileNames": true,
-
- "strict": true,
- "noImplicitAny": true,
- "strictNullChecks": true,
- "strictFunctionTypes": true,
- "strictBindCallApply": true,
- "strictPropertyInitialization": true,
- "noImplicitThis": true,
- "alwaysStrict": true,
- "exactOptionalPropertyTypes": true,
- "noUncheckedIndexedAccess": true,
- "noImplicitOverride": true,
- "noPropertyAccessFromIndexSignature": true,
-
- "skipLibCheck": true
+ "sourceMap": true
+ },
+ "tsc-alias": {
+ "resolveFullPaths": true,
+ "fileExtensions": {
+ "inputGlob": "{mjs,cjs,js,jsx,mts,cts,ts,tsx}"
+ },
+ "replacers": {
+ "replace-absolute-imports": {
+ "enabled": true,
+ "file": "./scripts/replace-self-referencing-imports.js"
+ }
+ }
}
}
diff --git a/tsconfig.json b/tsconfig.json
index c663d7307..82ea3f3e3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,6 @@
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
- "rootDir": "./src",
"baseUrl": "./",
"paths": {
"openai/_shims/*": ["src/_shims/*.node"],
@@ -14,12 +13,8 @@
"openai/*": ["src/*"],
"digest-fetch": ["./typings/digest-fetch"]
},
+ "noEmit": true,
- "declaration": true,
- "declarationMap": true,
- "outDir": "dist",
- "pretty": true,
- "sourceMap": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,