basant307/AI_Governance_Project
048
1// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.2var _OpenAI_instances, _a, _OpenAI_encoder, _OpenAI_baseURLOverridden;3import { __classPrivateFieldGet, __classPrivateFieldSet } from "./internal/tslib.mjs";4import { uuid4 } from "./internal/utils/uuid.mjs";5import { validatePositiveInteger, isAbsoluteURL, safeJSON } from "./internal/utils/values.mjs";6import { sleep } from "./internal/utils/sleep.mjs";7import { castToError, isAbortError } from "./internal/errors.mjs";8import { getPlatformHeaders } from "./internal/detect-platform.mjs";9import * as Shims from "./internal/shims.mjs";10import * as Opts from "./internal/request-options.mjs";11import * as qs from "./internal/qs/index.mjs";12import { VERSION } from "./version.mjs";13import * as Errors from "./core/error.mjs";14import * as Pagination from "./core/pagination.mjs";15import * as Uploads from "./core/uploads.mjs";16import * as API from "./resources/index.mjs";17import { APIPromise } from "./core/api-promise.mjs";18import { Batches, } from "./resources/batches.mjs";19import { Completions, } from "./resources/completions.mjs";20import { Embeddings, } from "./resources/embeddings.mjs";21import { Files, } from "./resources/files.mjs";22import { Images, } from "./resources/images.mjs";23import { Models } from "./resources/models.mjs";24import { Moderations, } from "./resources/moderations.mjs";25import { Webhooks } from "./resources/webhooks.mjs";26import { Audio } from "./resources/audio/audio.mjs";27import { Beta } from "./resources/beta/beta.mjs";28import { Chat } from "./resources/chat/chat.mjs";29import { Containers, } from "./resources/containers/containers.mjs";30import { Evals, } from "./resources/evals/evals.mjs";31import { FineTuning } from "./resources/fine-tuning/fine-tuning.mjs";32import { Graders } from "./resources/graders/graders.mjs";33import { Responses } from "./resources/responses/responses.mjs";34import { Uploads as UploadsAPIUploads, } from "./resources/uploads/uploads.mjs";35import { VectorStores, } from "./resources/vector-stores/vector-stores.mjs";36import { isRunningInBrowser } from "./internal/detect-platform.mjs";37import { buildHeaders } from "./internal/headers.mjs";38import { readEnv } from "./internal/utils/env.mjs";39import { formatRequestDetails, loggerFor, parseLogLevel, } from "./internal/utils/log.mjs";40import { isEmptyObj } from "./internal/utils/values.mjs";41/**42 * API Client for interfacing with the OpenAI API.43 */44export class OpenAI {45 /**46 * API Client for interfacing with the OpenAI API.47 *48 * @param {string | undefined} [opts.apiKey=process.env['OPENAI_API_KEY'] ?? undefined]49 * @param {string | null | undefined} [opts.organization=process.env['OPENAI_ORG_ID'] ?? null]50 * @param {string | null | undefined} [opts.project=process.env['OPENAI_PROJECT_ID'] ?? null]51 * @param {string | null | undefined} [opts.webhookSecret=process.env['OPENAI_WEBHOOK_SECRET'] ?? null]52 * @param {string} [opts.baseURL=process.env['OPENAI_BASE_URL'] ?? https://api.openai.com/v1] - Override the default base URL for the API.53 * @param {number} [opts.timeout=10 minutes] - The maximum amount of time (in milliseconds) the client will wait for a response before timing out.54 * @param {MergedRequestInit} [opts.fetchOptions] - Additional `RequestInit` options to be passed to `fetch` calls.55 * @param {Fetch} [opts.fetch] - Specify a custom `fetch` function implementation.56 * @param {number} [opts.maxRetries=2] - The maximum number of times the client will retry a request.57 * @param {HeadersLike} opts.defaultHeaders - Default headers to include with every request to the API.58 * @param {Record<string, string | undefined>} opts.defaultQuery - Default query parameters to include with every request to the API.59 * @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.60 */61 constructor({ baseURL = readEnv('OPENAI_BASE_URL'), apiKey = readEnv('OPENAI_API_KEY'), organization = readEnv('OPENAI_ORG_ID') ?? null, project = readEnv('OPENAI_PROJECT_ID') ?? null, webhookSecret = readEnv('OPENAI_WEBHOOK_SECRET') ?? null, ...opts } = {}) {62 _OpenAI_instances.add(this);63 _OpenAI_encoder.set(this, void 0);64 this.completions = new API.Completions(this);65 this.chat = new API.Chat(this);66 this.embeddings = new API.Embeddings(this);67 this.files = new API.Files(this);68 this.images = new API.Images(this);69 this.audio = new API.Audio(this);70 this.moderations = new API.Moderations(this);71 this.models = new API.Models(this);72 this.fineTuning = new API.FineTuning(this);73 this.graders = new API.Graders(this);74 this.vectorStores = new API.VectorStores(this);75 this.webhooks = new API.Webhooks(this);76 this.beta = new API.Beta(this);77 this.batches = new API.Batches(this);78 this.uploads = new API.Uploads(this);79 this.responses = new API.Responses(this);80 this.evals = new API.Evals(this);81 this.containers = new API.Containers(this);82 if (apiKey === undefined) {83 throw new Errors.OpenAIError("The OPENAI_API_KEY environment variable is missing or empty; either provide it, or instantiate the OpenAI client with an apiKey option, like new OpenAI({ apiKey: 'My API Key' }).");84 }85 const options = {86 apiKey,87 organization,88 project,89 webhookSecret,90 ...opts,91 baseURL: baseURL || `https://api.openai.com/v1`,92 };93 if (!options.dangerouslyAllowBrowser && isRunningInBrowser()) {94 throw new Errors.OpenAIError("It looks like you're running in a browser-like environment.\n\nThis is disabled by default, as it risks exposing your secret API credentials to attackers.\nIf you understand the risks and have appropriate mitigations in place,\nyou can set the `dangerouslyAllowBrowser` option to `true`, e.g.,\n\nnew OpenAI({ apiKey, dangerouslyAllowBrowser: true });\n\nhttps://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety\n");95 }96 this.baseURL = options.baseURL;97 this.timeout = options.timeout ?? _a.DEFAULT_TIMEOUT /* 10 minutes */;98 this.logger = options.logger ?? console;99 const defaultLogLevel = 'warn';100 // Set default logLevel early so that we can log a warning in parseLogLevel.101 this.logLevel = defaultLogLevel;102 this.logLevel =103 parseLogLevel(options.logLevel, 'ClientOptions.logLevel', this) ??104 parseLogLevel(readEnv('OPENAI_LOG'), "process.env['OPENAI_LOG']", this) ??105 defaultLogLevel;106 this.fetchOptions = options.fetchOptions;107 this.maxRetries = options.maxRetries ?? 2;108 this.fetch = options.fetch ?? Shims.getDefaultFetch();109 __classPrivateFieldSet(this, _OpenAI_encoder, Opts.FallbackEncoder, "f");110 this._options = options;111 this.apiKey = apiKey;112 this.organization = organization;113 this.project = project;114 this.webhookSecret = webhookSecret;115 }116 /**117 * Create a new client instance re-using the same options given to the current client with optional overriding.118 */119 withOptions(options) {120 const client = new this.constructor({121 ...this._options,122 baseURL: this.baseURL,123 maxRetries: this.maxRetries,124 timeout: this.timeout,125 logger: this.logger,126 logLevel: this.logLevel,127 fetch: this.fetch,128 fetchOptions: this.fetchOptions,129 apiKey: this.apiKey,130 organization: this.organization,131 project: this.project,132 webhookSecret: this.webhookSecret,133 ...options,134 });135 return client;136 }137 defaultQuery() {138 return this._options.defaultQuery;139 }140 validateHeaders({ values, nulls }) {141 return;142 }143 async authHeaders(opts) {144 return buildHeaders([{ Authorization: `Bearer ${this.apiKey}` }]);145 }146 stringifyQuery(query) {147 return qs.stringify(query, { arrayFormat: 'brackets' });148 }149 getUserAgent() {150 return `${this.constructor.name}/JS ${VERSION}`;151 }152 defaultIdempotencyKey() {153 return `stainless-node-retry-${uuid4()}`;154 }155 makeStatusError(status, error, message, headers) {156 return Errors.APIError.generate(status, error, message, headers);157 }158 buildURL(path, query, defaultBaseURL) {159 const baseURL = (!__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL) || this.baseURL;160 const url = isAbsoluteURL(path) ?161 new URL(path)162 : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));163 const defaultQuery = this.defaultQuery();164 if (!isEmptyObj(defaultQuery)) {165 query = { ...defaultQuery, ...query };166 }167 if (typeof query === 'object' && query && !Array.isArray(query)) {168 url.search = this.stringifyQuery(query);169 }170 return url.toString();171 }172 /**173 * Used as a callback for mutating the given `FinalRequestOptions` object.174 */175 async prepareOptions(options) { }176 /**177 * Used as a callback for mutating the given `RequestInit` object.178 *179 * This is useful for cases where you want to add certain headers based off of180 * the request properties, e.g. `method` or `url`.181 */182 async prepareRequest(request, { url, options }) { }183 get(path, opts) {184 return this.methodRequest('get', path, opts);185 }186 post(path, opts) {187 return this.methodRequest('post', path, opts);188 }189 patch(path, opts) {190 return this.methodRequest('patch', path, opts);191 }192 put(path, opts) {193 return this.methodRequest('put', path, opts);194 }195 delete(path, opts) {196 return this.methodRequest('delete', path, opts);197 }198 methodRequest(method, path, opts) {199 return this.request(Promise.resolve(opts).then((opts) => {200 return { method, path, ...opts };201 }));202 }203 request(options, remainingRetries = null) {204 return new APIPromise(this, this.makeRequest(options, remainingRetries, undefined));205 }206 async makeRequest(optionsInput, retriesRemaining, retryOfRequestLogID) {207 const options = await optionsInput;208 const maxRetries = options.maxRetries ?? this.maxRetries;209 if (retriesRemaining == null) {210 retriesRemaining = maxRetries;211 }212 await this.prepareOptions(options);213 const { req, url, timeout } = await this.buildRequest(options, {214 retryCount: maxRetries - retriesRemaining,215 });216 await this.prepareRequest(req, { url, options });217 /** Not an API request ID, just for correlating local log entries. */218 const requestLogID = 'log_' + ((Math.random() * (1 << 24)) | 0).toString(16).padStart(6, '0');219 const retryLogStr = retryOfRequestLogID === undefined ? '' : `, retryOf: ${retryOfRequestLogID}`;220 const startTime = Date.now();221 loggerFor(this).debug(`[${requestLogID}] sending request`, formatRequestDetails({222 retryOfRequestLogID,223 method: options.method,224 url,225 options,226 headers: req.headers,227 }));228 if (options.signal?.aborted) {229 throw new Errors.APIUserAbortError();230 }231 const controller = new AbortController();232 const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError);233 const headersTime = Date.now();234 if (response instanceof Error) {235 const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;236 if (options.signal?.aborted) {237 throw new Errors.APIUserAbortError();238 }239 // detect native connection timeout errors240 // deno throws "TypeError: error sending request for url (https://example/): client error (Connect): tcp connect error: Operation timed out (os error 60): Operation timed out (os error 60)"241 // undici throws "TypeError: fetch failed" with cause "ConnectTimeoutError: Connect Timeout Error (attempted address: example:443, timeout: 1ms)"242 // others do not provide enough information to distinguish timeouts from other connection errors243 const isTimeout = isAbortError(response) ||244 /timed? ?out/i.test(String(response) + ('cause' in response ? String(response.cause) : ''));245 if (retriesRemaining) {246 loggerFor(this).info(`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - ${retryMessage}`);247 loggerFor(this).debug(`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (${retryMessage})`, formatRequestDetails({248 retryOfRequestLogID,249 url,250 durationMs: headersTime - startTime,251 message: response.message,252 }));253 return this.retryRequest(options, retriesRemaining, retryOfRequestLogID ?? requestLogID);254 }255 loggerFor(this).info(`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - error; no more retries left`);256 loggerFor(this).debug(`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (error; no more retries left)`, formatRequestDetails({257 retryOfRequestLogID,258 url,259 durationMs: headersTime - startTime,260 message: response.message,261 }));262 if (isTimeout) {263 throw new Errors.APIConnectionTimeoutError();264 }265 throw new Errors.APIConnectionError({ cause: response });266 }267 const specialHeaders = [...response.headers.entries()]268 .filter(([name]) => name === 'x-request-id')269 .map(([name, value]) => ', ' + name + ': ' + JSON.stringify(value))270 .join('');271 const responseInfo = `[${requestLogID}${retryLogStr}${specialHeaders}] ${req.method} ${url} ${response.ok ? 'succeeded' : 'failed'} with status ${response.status} in ${headersTime - startTime}ms`;272 if (!response.ok) {273 const shouldRetry = await this.shouldRetry(response);274 if (retriesRemaining && shouldRetry) {275 const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;276 // We don't need the body of this response.277 await Shims.CancelReadableStream(response.body);278 loggerFor(this).info(`${responseInfo} - ${retryMessage}`);279 loggerFor(this).debug(`[${requestLogID}] response error (${retryMessage})`, formatRequestDetails({280 retryOfRequestLogID,281 url: response.url,282 status: response.status,283 headers: response.headers,284 durationMs: headersTime - startTime,285 }));286 return this.retryRequest(options, retriesRemaining, retryOfRequestLogID ?? requestLogID, response.headers);287 }288 const retryMessage = shouldRetry ? `error; no more retries left` : `error; not retryable`;289 loggerFor(this).info(`${responseInfo} - ${retryMessage}`);290 const errText = await response.text().catch((err) => castToError(err).message);291 const errJSON = safeJSON(errText);292 const errMessage = errJSON ? undefined : errText;293 loggerFor(this).debug(`[${requestLogID}] response error (${retryMessage})`, formatRequestDetails({294 retryOfRequestLogID,295 url: response.url,296 status: response.status,297 headers: response.headers,298 message: errMessage,299 durationMs: Date.now() - startTime,300 }));301 const err = this.makeStatusError(response.status, errJSON, errMessage, response.headers);302 throw err;303 }304 loggerFor(this).info(responseInfo);305 loggerFor(this).debug(`[${requestLogID}] response start`, formatRequestDetails({306 retryOfRequestLogID,307 url: response.url,308 status: response.status,309 headers: response.headers,310 durationMs: headersTime - startTime,311 }));312 return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };313 }314 getAPIList(path, Page, opts) {315 return this.requestAPIList(Page, { method: 'get', path, ...opts });316 }317 requestAPIList(Page, options) {318 const request = this.makeRequest(options, null, undefined);319 return new Pagination.PagePromise(this, request, Page);320 }321 async fetchWithTimeout(url, init, ms, controller) {322 const { signal, method, ...options } = init || {};323 if (signal)324 signal.addEventListener('abort', () => controller.abort());325 const timeout = setTimeout(() => controller.abort(), ms);326 const isReadableBody = (globalThis.ReadableStream && options.body instanceof globalThis.ReadableStream) ||327 (typeof options.body === 'object' && options.body !== null && Symbol.asyncIterator in options.body);328 const fetchOptions = {329 signal: controller.signal,330 ...(isReadableBody ? { duplex: 'half' } : {}),331 method: 'GET',332 ...options,333 };334 if (method) {335 // Custom methods like 'patch' need to be uppercased336 // See https://github.com/nodejs/undici/issues/2294337 fetchOptions.method = method.toUpperCase();338 }339 try {340 // use undefined this binding; fetch errors if bound to something else in browser/cloudflare341 return await this.fetch.call(undefined, url, fetchOptions);342 }343 finally {344 clearTimeout(timeout);345 }346 }347 async shouldRetry(response) {348 // Note this is not a standard header.349 const shouldRetryHeader = response.headers.get('x-should-retry');350 // If the server explicitly says whether or not to retry, obey.351 if (shouldRetryHeader === 'true')352 return true;353 if (shouldRetryHeader === 'false')354 return false;355 // Retry on request timeouts.356 if (response.status === 408)357 return true;358 // Retry on lock timeouts.359 if (response.status === 409)360 return true;361 // Retry on rate limits.362 if (response.status === 429)363 return true;364 // Retry internal errors.365 if (response.status >= 500)366 return true;367 return false;368 }369 async retryRequest(options, retriesRemaining, requestLogID, responseHeaders) {370 let timeoutMillis;371 // Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it.372 const retryAfterMillisHeader = responseHeaders?.get('retry-after-ms');373 if (retryAfterMillisHeader) {374 const timeoutMs = parseFloat(retryAfterMillisHeader);375 if (!Number.isNaN(timeoutMs)) {376 timeoutMillis = timeoutMs;377 }378 }379 // About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After380 const retryAfterHeader = responseHeaders?.get('retry-after');381 if (retryAfterHeader && !timeoutMillis) {382 const timeoutSeconds = parseFloat(retryAfterHeader);383 if (!Number.isNaN(timeoutSeconds)) {384 timeoutMillis = timeoutSeconds * 1000;385 }386 else {387 timeoutMillis = Date.parse(retryAfterHeader) - Date.now();388 }389 }390 // If the API asks us to wait a certain amount of time (and it's a reasonable amount),391 // just do what it says, but otherwise calculate a default392 if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) {393 const maxRetries = options.maxRetries ?? this.maxRetries;394 timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);395 }396 await sleep(timeoutMillis);397 return this.makeRequest(options, retriesRemaining - 1, requestLogID);398 }399 calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries) {400 const initialRetryDelay = 0.5;401 const maxRetryDelay = 8.0;402 const numRetries = maxRetries - retriesRemaining;403 // Apply exponential backoff, but not more than the max.404 const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay);405 // Apply some jitter, take up to at most 25 percent of the retry time.406 const jitter = 1 - Math.random() * 0.25;407 return sleepSeconds * jitter * 1000;408 }409 async buildRequest(inputOptions, { retryCount = 0 } = {}) {410 const options = { ...inputOptions };411 const { method, path, query, defaultBaseURL } = options;412 const url = this.buildURL(path, query, defaultBaseURL);413 if ('timeout' in options)414 validatePositiveInteger('timeout', options.timeout);415 options.timeout = options.timeout ?? this.timeout;416 const { bodyHeaders, body } = this.buildBody({ options });417 const reqHeaders = await this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount });418 const req = {419 method,420 headers: reqHeaders,421 ...(options.signal && { signal: options.signal }),422 ...(globalThis.ReadableStream &&423 body instanceof globalThis.ReadableStream && { duplex: 'half' }),424 ...(body && { body }),425 ...(this.fetchOptions ?? {}),426 ...(options.fetchOptions ?? {}),427 };428 return { req, url, timeout: options.timeout };429 }430 async buildHeaders({ options, method, bodyHeaders, retryCount, }) {431 let idempotencyHeaders = {};432 if (this.idempotencyHeader && method !== 'get') {433 if (!options.idempotencyKey)434 options.idempotencyKey = this.defaultIdempotencyKey();435 idempotencyHeaders[this.idempotencyHeader] = options.idempotencyKey;436 }437 const headers = buildHeaders([438 idempotencyHeaders,439 {440 Accept: 'application/json',441 'User-Agent': this.getUserAgent(),442 'X-Stainless-Retry-Count': String(retryCount),443 ...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}),444 ...getPlatformHeaders(),445 'OpenAI-Organization': this.organization,446 'OpenAI-Project': this.project,447 },448 await this.authHeaders(options),449 this._options.defaultHeaders,450 bodyHeaders,451 options.headers,452 ]);453 this.validateHeaders(headers);454 return headers.values;455 }456 buildBody({ options: { body, headers: rawHeaders } }) {457 if (!body) {458 return { bodyHeaders: undefined, body: undefined };459 }460 const headers = buildHeaders([rawHeaders]);461 if (462 // Pass raw type verbatim463 ArrayBuffer.isView(body) ||464 body instanceof ArrayBuffer ||465 body instanceof DataView ||466 (typeof body === 'string' &&467 // Preserve legacy string encoding behavior for now468 headers.values.has('content-type')) ||469 // `Blob` is superset of `File`470 body instanceof Blob ||471 // `FormData` -> `multipart/form-data`472 body instanceof FormData ||473 // `URLSearchParams` -> `application/x-www-form-urlencoded`474 body instanceof URLSearchParams ||475 // Send chunked stream (each chunk has own `length`)476 (globalThis.ReadableStream && body instanceof globalThis.ReadableStream)) {477 return { bodyHeaders: undefined, body: body };478 }479 else if (typeof body === 'object' &&480 (Symbol.asyncIterator in body ||481 (Symbol.iterator in body && 'next' in body && typeof body.next === 'function'))) {482 return { bodyHeaders: undefined, body: Shims.ReadableStreamFrom(body) };483 }484 else {485 return __classPrivateFieldGet(this, _OpenAI_encoder, "f").call(this, { body, headers });486 }487 }488}489_a = OpenAI, _OpenAI_encoder = new WeakMap(), _OpenAI_instances = new WeakSet(), _OpenAI_baseURLOverridden = function _OpenAI_baseURLOverridden() {490 return this.baseURL !== 'https://api.openai.com/v1';491};492OpenAI.OpenAI = _a;493OpenAI.DEFAULT_TIMEOUT = 600000; // 10 minutes494OpenAI.OpenAIError = Errors.OpenAIError;495OpenAI.APIError = Errors.APIError;496OpenAI.APIConnectionError = Errors.APIConnectionError;497OpenAI.APIConnectionTimeoutError = Errors.APIConnectionTimeoutError;498OpenAI.APIUserAbortError = Errors.APIUserAbortError;499OpenAI.NotFoundError = Errors.NotFoundError;500OpenAI.ConflictError = Errors.ConflictError;501OpenAI.RateLimitError = Errors.RateLimitError;502OpenAI.BadRequestError = Errors.BadRequestError;503OpenAI.AuthenticationError = Errors.AuthenticationError;504OpenAI.InternalServerError = Errors.InternalServerError;505OpenAI.PermissionDeniedError = Errors.PermissionDeniedError;506OpenAI.UnprocessableEntityError = Errors.UnprocessableEntityError;507OpenAI.InvalidWebhookSignatureError = Errors.InvalidWebhookSignatureError;508OpenAI.toFile = Uploads.toFile;509OpenAI.Completions = Completions;510OpenAI.Chat = Chat;511OpenAI.Embeddings = Embeddings;512OpenAI.Files = Files;513OpenAI.Images = Images;514OpenAI.Audio = Audio;515OpenAI.Moderations = Moderations;516OpenAI.Models = Models;517OpenAI.FineTuning = FineTuning;518OpenAI.Graders = Graders;519OpenAI.VectorStores = VectorStores;520OpenAI.Webhooks = Webhooks;521OpenAI.Beta = Beta;522OpenAI.Batches = Batches;523OpenAI.Uploads = UploadsAPIUploads;524OpenAI.Responses = Responses;525OpenAI.Evals = Evals;526OpenAI.Containers = Containers;527//# sourceMappingURL=client.mjs.map