diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ce65b73e4..448d10afc 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.3.0" + ".": "5.4.0" } diff --git a/.stats.yml b/.stats.yml index c9e264655..feda32cff 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-3ae9c18dd7ccfc3ac5206f24394665f563a19015cfa8847b2801a2694d012abc.yml -openapi_spec_hash: 48175b03b58805cd5c80793c66fd54e5 -config_hash: 4caff63b74a41f71006987db702f2918 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9e41d2d5471d2c28bff0d616f4476f5b0e6c541ef4cb51bdaaef5fdf5e13c8b2.yml +openapi_spec_hash: 86f765e18d00e32cf2ce9db7ab84d946 +config_hash: fd2af1d5eff0995bb7dc02ac9a34851d diff --git a/CHANGELOG.md b/CHANGELOG.md index 1446910c6..a3b2d6ccb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 5.4.0 (2025-06-16) + +Full Changelog: [v5.3.0...v5.4.0](https://github.com/openai/openai-node/compare/v5.3.0...v5.4.0) + +### Features + +* **api:** add reusable prompt IDs ([c720bb3](https://github.com/openai/openai-node/commit/c720bb3fb909cdef1cc679df38357f046d3d2756)) +* **client:** add support for endpoint-specific base URLs ([05f558b](https://github.com/openai/openai-node/commit/05f558bcdd362ae56000fe515a24593363d59e83)) + + +### Bug Fixes + +* publish script — handle NPM errors correctly ([a803cce](https://github.com/openai/openai-node/commit/a803cce6d44116eaba34f2bd7cb0f5d8f5c72be8)) + + +### Chores + +* **client:** refactor imports ([9eb4470](https://github.com/openai/openai-node/commit/9eb44703432d7e22290564013f8e1798c82918a3)) +* **internal:** add pure annotations, make base APIResource abstract ([418eb02](https://github.com/openai/openai-node/commit/418eb02e3ebe3ef58d851405f9eb5cae275194b4)) + ## 5.3.0 (2025-06-10) Full Changelog: [v5.2.0...v5.3.0](https://github.com/openai/openai-node/compare/v5.2.0...v5.3.0) diff --git a/api.md b/api.md index 269566a57..4e2de3252 100644 --- a/api.md +++ b/api.md @@ -677,6 +677,7 @@ Types: - ResponseOutputRefusal - ResponseOutputText - ResponseOutputTextAnnotationAddedEvent +- ResponsePrompt - ResponseQueuedEvent - ResponseReasoningDeltaEvent - ResponseReasoningDoneEvent diff --git a/bin/publish-npm b/bin/publish-npm index 2505decac..fa2243d24 100644 --- a/bin/publish-npm +++ b/bin/publish-npm @@ -7,15 +7,35 @@ npm config set '//registry.npmjs.org/:_authToken' "$NPM_TOKEN" yarn build cd dist +# Get package name and version from package.json +PACKAGE_NAME="$(jq -r -e '.name' ./package.json)" +VERSION="$(jq -r -e '.version' ./package.json)" + # Get latest version from npm # -# If the package doesn't exist, yarn will return -# {"type":"error","data":"Received invalid response from npm."} -# where .data.version doesn't exist so LAST_VERSION will be an empty string. -LAST_VERSION="$(yarn info --json 2> /dev/null | jq -r '.data.version')" - -# Get current version from package.json -VERSION="$(node -p "require('./package.json').version")" +# If the package doesn't exist, npm will return: +# { +# "error": { +# "code": "E404", +# "summary": "Unpublished on 2025-06-05T09:54:53.528Z", +# "detail": "'the_package' is not in this registry..." +# } +# } +NPM_INFO="$(npm view "$PACKAGE_NAME" version --json 2>/dev/null || true)" + +# Check if we got an E404 error +if echo "$NPM_INFO" | jq -e '.error.code == "E404"' > /dev/null 2>&1; then + # Package doesn't exist yet, no last version + LAST_VERSION="" +elif echo "$NPM_INFO" | jq -e '.error' > /dev/null 2>&1; then + # Report other errors + echo "ERROR: npm returned unexpected data:" + echo "$NPM_INFO" + exit 1 +else + # Success - get the version + LAST_VERSION=$(echo "$NPM_INFO" | jq -r '.') # strip quotes +fi # Check if current version is pre-release (e.g. alpha / beta / rc) CURRENT_IS_PRERELEASE=false diff --git a/jsr.json b/jsr.json index d079c8f25..4d018d709 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@openai/openai", - "version": "5.3.0", + "version": "5.4.0", "exports": { ".": "./index.ts", "./helpers/zod": "./helpers/zod.ts", diff --git a/package.json b/package.json index 56e5b2352..0aeb9e260 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openai", - "version": "5.3.0", + "version": "5.4.0", "description": "The official TypeScript library for the OpenAI API", "author": "OpenAI ", "types": "dist/index.d.ts", @@ -45,7 +45,7 @@ "publint": "^0.2.12", "ts-jest": "^29.1.0", "ts-node": "^10.5.0", - "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz", "tsconfig-paths": "^4.0.0", "typescript": "5.8.3", "ws": "^8.18.0", diff --git a/scripts/build b/scripts/build index beb4da8f7..1ee175252 100755 --- a/scripts/build +++ b/scripts/build @@ -31,7 +31,7 @@ fi node scripts/utils/make-dist-package-json.cjs > dist/package.json # build to .js/.mjs/.d.ts files -npm exec tsc-multi +./node_modules/.bin/tsc-multi # we need to patch index.js so that `new module.exports()` works for cjs backwards # compat. No way to get that from index.ts because it would cause compile errors # when building .mjs diff --git a/src/client.ts b/src/client.ts index 9ebdf8f02..f33e13ead 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5,7 +5,6 @@ import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestIni import { uuid4 } from './internal/utils/uuid'; import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values'; import { sleep } from './internal/utils/sleep'; -import { type Logger, type LogLevel, parseLogLevel } from './internal/utils/log'; export type { Logger, LogLevel } from './internal/utils/log'; import { castToError, isAbortError } from './internal/errors'; import type { APIResponseProps } from './internal/parse'; @@ -20,10 +19,6 @@ import { AbstractPage, type CursorPageParams, CursorPageResponse, PageResponse } import * as Uploads from './core/uploads'; import * as API from './resources/index'; import { APIPromise } from './core/api-promise'; -import { type Fetch } from './internal/builtin-types'; -import { isRunningInBrowser } from './internal/detect-platform'; -import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; -import { FinalRequestOptions, RequestOptions } from './internal/request-options'; import { Batch, BatchCreateParams, @@ -79,9 +74,6 @@ import { ModerationTextInput, Moderations, } from './resources/moderations'; -import { readEnv } from './internal/utils/env'; -import { formatRequestDetails, loggerFor } from './internal/utils/log'; -import { isEmptyObj } from './internal/utils/values'; import { Audio, AudioModel, AudioResponseFormat } from './resources/audio/audio'; import { Beta } from './resources/beta/beta'; import { Chat } from './resources/chat/chat'; @@ -174,6 +166,19 @@ import { ChatCompletionUserMessageParam, ChatCompletionsPage, } from './resources/chat/completions/completions'; +import { type Fetch } from './internal/builtin-types'; +import { isRunningInBrowser } from './internal/detect-platform'; +import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; +import { FinalRequestOptions, RequestOptions } from './internal/request-options'; +import { readEnv } from './internal/utils/env'; +import { + type LogLevel, + type Logger, + formatRequestDetails, + loggerFor, + parseLogLevel, +} from './internal/utils/log'; +import { isEmptyObj } from './internal/utils/values'; export interface ClientOptions { /** @@ -367,6 +372,13 @@ export class OpenAI { }); } + /** + * Check whether the base URL is set to its default. + */ + #baseURLOverridden(): boolean { + return this.baseURL !== 'https://api.openai.com/v1'; + } + protected defaultQuery(): Record | undefined { return this._options.defaultQuery; } @@ -400,11 +412,16 @@ export class OpenAI { return Errors.APIError.generate(status, error, message, headers); } - buildURL(path: string, query: Record | null | undefined): string { + buildURL( + path: string, + query: Record | null | undefined, + defaultBaseURL?: string | undefined, + ): string { + const baseURL = (!this.#baseURLOverridden() && defaultBaseURL) || this.baseURL; const url = isAbsoluteURL(path) ? new URL(path) - : new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); + : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); const defaultQuery = this.defaultQuery(); if (!isEmptyObj(defaultQuery)) { @@ -768,9 +785,9 @@ export class OpenAI { { retryCount = 0 }: { retryCount?: number } = {}, ): { req: FinalizedRequestInit; url: string; timeout: number } { const options = { ...inputOptions }; - const { method, path, query } = options; + const { method, path, query, defaultBaseURL } = options; - const url = this.buildURL(path!, query as Record); + const url = this.buildURL(path!, query as Record, defaultBaseURL); if ('timeout' in options) validatePositiveInteger('timeout', options.timeout); options.timeout = options.timeout ?? this.timeout; const { bodyHeaders, body } = this.buildBody({ options }); diff --git a/src/core/resource.ts b/src/core/resource.ts index 8ba97f70a..d9a191e58 100644 --- a/src/core/resource.ts +++ b/src/core/resource.ts @@ -2,7 +2,7 @@ import type { OpenAI } from '../client'; -export class APIResource { +export abstract class APIResource { protected _client: OpenAI; constructor(client: OpenAI) { diff --git a/src/internal/headers.ts b/src/internal/headers.ts index 5cc03ce32..c724a9d22 100644 --- a/src/internal/headers.ts +++ b/src/internal/headers.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { isReadonlyArray } from './utils/values'; + type HeaderValue = string | undefined | null; export type HeadersLike = | Headers @@ -9,7 +11,7 @@ export type HeadersLike = | null | NullableHeaders; -const brand_privateNullableHeaders = Symbol('brand.privateNullableHeaders'); +const brand_privateNullableHeaders = /* @__PURE__ */ Symbol('brand.privateNullableHeaders'); /** * @internal @@ -25,8 +27,6 @@ export type NullableHeaders = { nulls: Set; }; -const isArray = Array.isArray as (val: unknown) => val is readonly unknown[]; - function* iterateHeaders(headers: HeadersLike): IterableIterator { if (!headers) return; @@ -43,7 +43,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator; if (headers instanceof Headers) { iter = headers.entries(); - } else if (isArray(headers)) { + } else if (isReadonlyArray(headers)) { iter = headers; } else { shouldClear = true; @@ -52,7 +52,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator String(v); export const formatters: Record string> = { RFC1738: (v: PropertyKey) => String(v).replace(/%20/g, '+'), - RFC3986: (v: PropertyKey) => String(v), + RFC3986: default_formatter, }; export const RFC1738 = 'RFC1738'; export const RFC3986 = 'RFC3986'; diff --git a/src/internal/qs/stringify.ts b/src/internal/qs/stringify.ts index 67497561a..7e71387f5 100644 --- a/src/internal/qs/stringify.ts +++ b/src/internal/qs/stringify.ts @@ -1,8 +1,7 @@ -import { encode, is_buffer, maybe_map } from './utils'; -import { default_format, formatters } from './formats'; +import { encode, is_buffer, maybe_map, has } from './utils'; +import { default_format, default_formatter, formatters } from './formats'; import type { NonNullableProperties, StringifyOptions } from './types'; - -const has = Object.prototype.hasOwnProperty; +import { isArray } from '../utils/values'; const array_prefix_generators = { brackets(prefix: PropertyKey) { @@ -17,13 +16,11 @@ const array_prefix_generators = { }, }; -const is_array = Array.isArray; -const push = Array.prototype.push; const push_to_array = function (arr: any[], value_or_array: any) { - push.apply(arr, is_array(value_or_array) ? value_or_array : [value_or_array]); + Array.prototype.push.apply(arr, isArray(value_or_array) ? value_or_array : [value_or_array]); }; -const to_ISO = Date.prototype.toISOString; +let toISOString; const defaults = { addQueryPrefix: false, @@ -38,11 +35,11 @@ const defaults = { encoder: encode, encodeValuesOnly: false, format: default_format, - formatter: formatters[default_format], + formatter: default_formatter, /** @deprecated */ indices: false, serializeDate(date) { - return to_ISO.call(date); + return (toISOString ??= Function.prototype.call.bind(Date.prototype.toISOString))(date); }, skipNulls: false, strictNullHandling: false, @@ -105,7 +102,7 @@ function inner_stringify( obj = filter(prefix, obj); } else if (obj instanceof Date) { obj = serializeDate?.(obj); - } else if (generateArrayPrefix === 'comma' && is_array(obj)) { + } else if (generateArrayPrefix === 'comma' && isArray(obj)) { obj = maybe_map(obj, function (value) { if (value instanceof Date) { return serializeDate?.(value); @@ -148,14 +145,14 @@ function inner_stringify( } let obj_keys; - if (generateArrayPrefix === 'comma' && is_array(obj)) { + if (generateArrayPrefix === 'comma' && isArray(obj)) { // we need to join elements in if (encodeValuesOnly && encoder) { // @ts-expect-error values only obj = maybe_map(obj, encoder); } obj_keys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }]; - } else if (is_array(filter)) { + } else if (isArray(filter)) { obj_keys = filter; } else { const keys = Object.keys(obj); @@ -165,9 +162,9 @@ function inner_stringify( const encoded_prefix = encodeDotInKeys ? String(prefix).replace(/\./g, '%2E') : String(prefix); const adjusted_prefix = - commaRoundTrip && is_array(obj) && obj.length === 1 ? encoded_prefix + '[]' : encoded_prefix; + commaRoundTrip && isArray(obj) && obj.length === 1 ? encoded_prefix + '[]' : encoded_prefix; - if (allowEmptyArrays && is_array(obj) && obj.length === 0) { + if (allowEmptyArrays && isArray(obj) && obj.length === 0) { return adjusted_prefix + '[]'; } @@ -184,7 +181,7 @@ function inner_stringify( // @ts-ignore const encoded_key = allowDots && encodeDotInKeys ? (key as any).replace(/\./g, '%2E') : key; const key_prefix = - is_array(obj) ? + isArray(obj) ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(adjusted_prefix, encoded_key) : adjusted_prefix @@ -205,7 +202,7 @@ function inner_stringify( skipNulls, encodeDotInKeys, // @ts-ignore - generateArrayPrefix === 'comma' && encodeValuesOnly && is_array(obj) ? null : encoder, + generateArrayPrefix === 'comma' && encodeValuesOnly && isArray(obj) ? null : encoder, filter, sort, allowDots, @@ -244,7 +241,7 @@ function normalize_stringify_options( let format = default_format; if (typeof opts.format !== 'undefined') { - if (!has.call(formatters, opts.format)) { + if (!has(formatters, opts.format)) { throw new TypeError('Unknown format option provided.'); } format = opts.format; @@ -252,7 +249,7 @@ function normalize_stringify_options( const formatter = formatters[format]; let filter = defaults.filter; - if (typeof opts.filter === 'function' || is_array(opts.filter)) { + if (typeof opts.filter === 'function' || isArray(opts.filter)) { filter = opts.filter; } @@ -316,7 +313,7 @@ export function stringify(object: any, opts: StringifyOptions = {}) { if (typeof options.filter === 'function') { filter = options.filter; obj = filter('', obj); - } else if (is_array(options.filter)) { + } else if (isArray(options.filter)) { filter = options.filter; obj_keys = filter; } diff --git a/src/internal/qs/utils.ts b/src/internal/qs/utils.ts index 113b18fb9..4cd56579c 100644 --- a/src/internal/qs/utils.ts +++ b/src/internal/qs/utils.ts @@ -1,10 +1,13 @@ import { RFC1738 } from './formats'; import type { DefaultEncoder, Format } from './types'; +import { isArray } from '../utils/values'; -const has = Object.prototype.hasOwnProperty; -const is_array = Array.isArray; +export let has = (obj: object, key: PropertyKey): boolean => ( + (has = (Object as any).hasOwn ?? Function.prototype.call.bind(Object.prototype.hasOwnProperty)), + has(obj, key) +); -const hex_table = (() => { +const hex_table = /* @__PURE__ */ (() => { const array = []; for (let i = 0; i < 256; ++i) { array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); @@ -20,7 +23,7 @@ function compact_queue>(queue: Array<{ obj: T; pro const obj = item.obj[item.prop]; - if (is_array(obj)) { + if (isArray(obj)) { const compacted: unknown[] = []; for (let j = 0; j < obj.length; ++j) { @@ -56,13 +59,10 @@ export function merge( } if (typeof source !== 'object') { - if (is_array(target)) { + if (isArray(target)) { target.push(source); } else if (target && typeof target === 'object') { - if ( - (options && (options.plainObjects || options.allowPrototypes)) || - !has.call(Object.prototype, source) - ) { + if ((options && (options.plainObjects || options.allowPrototypes)) || !has(Object.prototype, source)) { target[source] = true; } } else { @@ -77,14 +77,14 @@ export function merge( } let mergeTarget = target; - if (is_array(target) && !is_array(source)) { + if (isArray(target) && !isArray(source)) { // @ts-ignore mergeTarget = array_to_object(target, options); } - if (is_array(target) && is_array(source)) { + if (isArray(target) && isArray(source)) { source.forEach(function (item, i) { - if (has.call(target, i)) { + if (has(target, i)) { const targetItem = target[i]; if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { target[i] = merge(targetItem, item, options); @@ -101,7 +101,7 @@ export function merge( return Object.keys(source).reduce(function (acc, key) { const value = source[key]; - if (has.call(acc, key)) { + if (has(acc, key)) { acc[key] = merge(acc[key], value, options); } else { acc[key] = value; @@ -254,7 +254,7 @@ export function combine(a: any, b: any) { } export function maybe_map(val: T[], fn: (v: T) => T) { - if (is_array(val)) { + if (isArray(val)) { const mapped = []; for (let i = 0; i < val.length; i += 1) { mapped.push(fn(val[i]!)); diff --git a/src/internal/request-options.ts b/src/internal/request-options.ts index 78daa4199..7ee5c622d 100644 --- a/src/internal/request-options.ts +++ b/src/internal/request-options.ts @@ -21,6 +21,7 @@ export type RequestOptions = { fetchOptions?: MergedRequestInit; signal?: AbortSignal | undefined | null; idempotencyKey?: string; + defaultBaseURL?: string | undefined; __metadata?: Record; __binaryResponse?: boolean | undefined; diff --git a/src/internal/uploads.ts b/src/internal/uploads.ts index fbc48b298..2f7271164 100644 --- a/src/internal/uploads.ts +++ b/src/internal/uploads.ts @@ -90,7 +90,7 @@ export const multipartFormRequestOptions = async ( return { ...opts, body: await createForm(opts.body, fetch) }; }; -const supportsFormDataMap = new WeakMap>(); +const supportsFormDataMap = /** @__PURE__ */ new WeakMap>(); /** * node-fetch doesn't support the global FormData object in recent node versions. Instead of sending diff --git a/src/internal/utils/log.ts b/src/internal/utils/log.ts index 1aec41276..e1df9cef6 100644 --- a/src/internal/utils/log.ts +++ b/src/internal/utils/log.ts @@ -58,7 +58,7 @@ const noopLogger = { debug: noop, }; -let cachedLoggers = new WeakMap(); +let cachedLoggers = /** @__PURE__ */ new WeakMap(); export function loggerFor(client: OpenAI): Logger { const logger = client.logger; diff --git a/src/internal/utils/path.ts b/src/internal/utils/path.ts index 0dceb10f1..122066855 100644 --- a/src/internal/utils/path.ts +++ b/src/internal/utils/path.ts @@ -60,4 +60,4 @@ export const createPathTagFunction = (pathEncoder = encodeURIPath) => /** * URI-encodes path params and ensures no unsafe /./ or /../ path segments are introduced. */ -export const path = createPathTagFunction(encodeURIPath); +export const path = /* @__PURE__ */ createPathTagFunction(encodeURIPath); diff --git a/src/internal/utils/values.ts b/src/internal/utils/values.ts index aee03b055..801974e84 100644 --- a/src/internal/utils/values.ts +++ b/src/internal/utils/values.ts @@ -9,6 +9,9 @@ export const isAbsoluteURL = (url: string): boolean => { return startsWithSchemeRegexp.test(url); }; +export let isArray = (val: unknown): val is unknown[] => ((isArray = Array.isArray), isArray(val)); +export let isReadonlyArray = isArray as (val: unknown) => val is readonly unknown[]; + /** Returns an object if the given value isn't an object, otherwise returns as-is */ export function maybeObj(x: unknown): object { if (typeof x !== 'object') { diff --git a/src/resources/chat/completions/completions.ts b/src/resources/chat/completions/completions.ts index 583deccf8..a88e27e98 100644 --- a/src/resources/chat/completions/completions.ts +++ b/src/resources/chat/completions/completions.ts @@ -291,7 +291,7 @@ export interface ChatCompletion { * When this parameter is set, the response body will include the `service_tier` * utilized. */ - service_tier?: 'auto' | 'default' | 'flex' | null; + service_tier?: 'auto' | 'default' | 'flex' | 'scale' | null; /** * This fingerprint represents the backend configuration that the model runs with. @@ -542,7 +542,7 @@ export interface ChatCompletionChunk { * When this parameter is set, the response body will include the `service_tier` * utilized. */ - service_tier?: 'auto' | 'default' | 'flex' | null; + service_tier?: 'auto' | 'default' | 'flex' | 'scale' | null; /** * This fingerprint represents the backend configuration that the model runs with. @@ -1464,7 +1464,7 @@ export interface ChatCompletionCreateParamsBase { * When this parameter is set, the response body will include the `service_tier` * utilized. */ - service_tier?: 'auto' | 'default' | 'flex' | null; + service_tier?: 'auto' | 'default' | 'flex' | 'scale' | null; /** * Not supported with latest reasoning models `o3` and `o4-mini`. diff --git a/src/resources/fine-tuning/jobs/jobs.ts b/src/resources/fine-tuning/jobs/jobs.ts index 1c43289e4..7e8f672e5 100644 --- a/src/resources/fine-tuning/jobs/jobs.ts +++ b/src/resources/fine-tuning/jobs/jobs.ts @@ -25,7 +25,7 @@ export class Jobs extends APIResource { * Response includes details of the enqueued job including job status and the name * of the fine-tuned models once complete. * - * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) + * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization) * * @example * ```ts @@ -42,7 +42,7 @@ export class Jobs extends APIResource { /** * Get info about a fine-tuning job. * - * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) + * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization) * * @example * ```ts @@ -449,7 +449,8 @@ export interface JobCreateParams { * [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input) * format. * - * See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + * See the + * [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization) * for more details. */ training_file: string; @@ -508,7 +509,8 @@ export interface JobCreateParams { * Your dataset must be formatted as a JSONL file. You must upload your file with * the purpose `fine-tune`. * - * See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + * See the + * [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization) * for more details. */ validation_file?: string | null; diff --git a/src/resources/images.ts b/src/resources/images.ts index fdc51abe3..23b7d1507 100644 --- a/src/resources/images.ts +++ b/src/resources/images.ts @@ -237,6 +237,20 @@ export interface ImageEditParams { */ n?: number | null; + /** + * The compression level (0-100%) for the generated images. This parameter is only + * supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + * defaults to 100. + */ + output_compression?: number | null; + + /** + * The format in which the generated images are returned. This parameter is only + * supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + * default value is `png`. + */ + output_format?: 'png' | 'jpeg' | 'webp' | null; + /** * The quality of the image that will be generated. `high`, `medium` and `low` are * only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. diff --git a/src/resources/responses/responses.ts b/src/resources/responses/responses.ts index a443f101a..1cb0788e0 100644 --- a/src/resources/responses/responses.ts +++ b/src/resources/responses/responses.ts @@ -358,14 +358,13 @@ export interface Response { incomplete_details: Response.IncompleteDetails | null; /** - * Inserts a system (or developer) message as the first item in the model's - * context. + * A system (or developer) message inserted into the model's context. * * When using along with `previous_response_id`, the instructions from a previous * response will not be carried over to the next response. This makes it simple to * swap out system (or developer) messages in new responses. */ - instructions: string | null; + instructions: string | Array | null; /** * Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -469,6 +468,12 @@ export interface Response { */ previous_response_id?: string | null; + /** + * Reference to a prompt template and its variables. + * [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + */ + prompt?: ResponsePrompt | null; + /** * **o-series models only** * @@ -496,7 +501,7 @@ export interface Response { * When this parameter is set, the response body will include the `service_tier` * utilized. */ - service_tier?: 'auto' | 'default' | 'flex' | null; + service_tier?: 'auto' | 'default' | 'flex' | 'scale' | null; /** * The status of the response generation. One of `completed`, `failed`, @@ -3483,6 +3488,29 @@ export interface ResponseOutputTextAnnotationAddedEvent { type: 'response.output_text_annotation.added'; } +/** + * Reference to a prompt template and its variables. + * [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + */ +export interface ResponsePrompt { + /** + * The unique identifier of the prompt template to use. + */ + id: string; + + /** + * Optional map of values to substitute in for variables in your prompt. The + * substitution values can either be strings, or other Response input types like + * images or files. + */ + variables?: Record | null; + + /** + * Optional version of the prompt template. + */ + version?: string | null; +} + /** * Emitted when a response is queued and waiting to be processed. */ @@ -4612,8 +4640,7 @@ export interface ResponseCreateParamsBase { include?: Array | null; /** - * Inserts a system (or developer) message as the first item in the model's - * context. + * A system (or developer) message inserted into the model's context. * * When using along with `previous_response_id`, the instructions from a previous * response will not be carried over to the next response. This makes it simple to @@ -4650,6 +4677,12 @@ export interface ResponseCreateParamsBase { */ previous_response_id?: string | null; + /** + * Reference to a prompt template and its variables. + * [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + */ + prompt?: ResponsePrompt | null; + /** * **o-series models only** * @@ -4677,7 +4710,7 @@ export interface ResponseCreateParamsBase { * When this parameter is set, the response body will include the `service_tier` * utilized. */ - service_tier?: 'auto' | 'default' | 'flex' | null; + service_tier?: 'auto' | 'default' | 'flex' | 'scale' | null; /** * Whether to store the generated model response for later retrieval via API. @@ -4922,6 +4955,7 @@ export declare namespace Responses { type ResponseOutputRefusal as ResponseOutputRefusal, type ResponseOutputText as ResponseOutputText, type ResponseOutputTextAnnotationAddedEvent as ResponseOutputTextAnnotationAddedEvent, + type ResponsePrompt as ResponsePrompt, type ResponseQueuedEvent as ResponseQueuedEvent, type ResponseReasoningDeltaEvent as ResponseReasoningDeltaEvent, type ResponseReasoningDoneEvent as ResponseReasoningDoneEvent, diff --git a/src/version.ts b/src/version.ts index b17877a92..0e5f6f831 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '5.3.0'; // x-release-please-version +export const VERSION = '5.4.0'; // x-release-please-version diff --git a/tests/api-resources/images.test.ts b/tests/api-resources/images.test.ts index ba62d993c..2fcfd34c8 100644 --- a/tests/api-resources/images.test.ts +++ b/tests/api-resources/images.test.ts @@ -54,6 +54,8 @@ describe('resource images', () => { mask: await toFile(Buffer.from('# my file contents'), 'README.md'), model: 'string', n: 1, + output_compression: 100, + output_format: 'png', quality: 'high', response_format: 'url', size: '1024x1024', diff --git a/tests/api-resources/responses/responses.test.ts b/tests/api-resources/responses/responses.test.ts index f1480f8c3..afe5a293a 100644 --- a/tests/api-resources/responses/responses.test.ts +++ b/tests/api-resources/responses/responses.test.ts @@ -30,6 +30,7 @@ describe('resource responses', () => { metadata: { foo: 'string' }, parallel_tool_calls: true, previous_response_id: 'previous_response_id', + prompt: { id: 'id', variables: { foo: 'string' }, version: 'version' }, reasoning: { effort: 'low', generate_summary: 'auto', summary: 'auto' }, service_tier: 'auto', store: true, diff --git a/tests/index.test.ts b/tests/index.test.ts index 401cc5c6e..e61902eea 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -310,6 +310,28 @@ describe('instantiate client', () => { const client = new OpenAI({ apiKey: 'My API Key' }); expect(client.baseURL).toEqual('https://api.openai.com/v1'); }); + + test('in request options', () => { + const client = new OpenAI({ apiKey: 'My API Key' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/option/foo', + ); + }); + + test('in request options overridden by client options', () => { + const client = new OpenAI({ apiKey: 'My API Key', baseURL: 'http://localhost:5000/client' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/client/foo', + ); + }); + + test('in request options overridden by env variable', () => { + process.env['OPENAI_BASE_URL'] = 'http://localhost:5000/env'; + const client = new OpenAI({ apiKey: 'My API Key' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/env/foo', + ); + }); }); test('maxRetries option is correctly set', () => { diff --git a/tsc-multi.json b/tsc-multi.json index 170bac7a4..384ddac5b 100644 --- a/tsc-multi.json +++ b/tsc-multi.json @@ -1,7 +1,15 @@ { "targets": [ - { "extname": ".js", "module": "commonjs", "shareHelpers": "internal/tslib.js" }, - { "extname": ".mjs", "module": "esnext", "shareHelpers": "internal/tslib.mjs" } + { + "extname": ".js", + "module": "commonjs", + "shareHelpers": "internal/tslib.js" + }, + { + "extname": ".mjs", + "module": "esnext", + "shareHelpers": "internal/tslib.mjs" + } ], "projects": ["tsconfig.build.json"] } diff --git a/yarn.lock b/yarn.lock index 110f00260..e95b98c26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3302,9 +3302,9 @@ ts-node@^10.5.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" -"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz": - version "1.1.4" - resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz#cbed459a9e902f5295ec3daaf1c7aa3b10427e55" +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz": + version "1.1.8" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz#f544b359b8f05e607771ffacc280e58201476b04" dependencies: debug "^4.3.7" fast-glob "^3.3.2"