diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7b04494d6..f6df5bd5c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.90.0" + ".": "4.91.0" } diff --git a/.stats.yml b/.stats.yml index 1e1104a06..f6a90d243 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6663c59193eb95b201e492de17dcbd5e126ba03d18ce66287a3e2c632ca56fe7.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: 9351ea829c2b41da3b48a38c934c92ee +config_hash: e25e31d8446b6bc0e3ef7103b6993cce diff --git a/CHANGELOG.md b/CHANGELOG.md index 89523001a..8cf3201bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 4.91.0 (2025-03-31) + +Full Changelog: [v4.90.0...v4.91.0](https://github.com/openai/openai-node/compare/v4.90.0...v4.91.0) + +### Features + +* **api:** add `get /responses/{response_id}/input_items` endpoint ([ef0e0ac](https://github.com/openai/openai-node/commit/ef0e0acd469379ae6f2745c83e6c6813ff7b4edc)) + + +### Performance Improvements + +* **embedding:** default embedding creation to base64 ([#1312](https://github.com/openai/openai-node/issues/1312)) ([e54530e](https://github.com/openai/openai-node/commit/e54530e4f6f00d7d74fc8636bbdb6f6280548750)), closes [#1310](https://github.com/openai/openai-node/issues/1310) + ## 4.90.0 (2025-03-27) Full Changelog: [v4.89.1...v4.90.0](https://github.com/openai/openai-node/compare/v4.89.1...v4.90.0) diff --git a/jsr.json b/jsr.json index 98c8e6959..4595ab4b7 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@openai/openai", - "version": "4.90.0", + "version": "4.91.0", "exports": { ".": "./index.ts", "./helpers/zod": "./helpers/zod.ts", diff --git a/package.json b/package.json index 408e50a73..089656265 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openai", - "version": "4.90.0", + "version": "4.91.0", "description": "The official TypeScript library for the OpenAI API", "author": "OpenAI ", "types": "dist/index.d.ts", diff --git a/src/core.ts b/src/core.ts index 0dedc53eb..a3f664906 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1287,6 +1287,27 @@ export const toBase64 = (str: string | null | undefined): string => { throw new OpenAIError('Cannot generate b64 string; Expected `Buffer` or `btoa` to be defined'); }; +/** + * Converts a Base64 encoded string to a Float32Array. + * @param base64Str - The Base64 encoded string. + * @returns An Array of numbers interpreted as Float32 values. + */ +export const toFloat32Array = (base64Str: string): Array => { + if (typeof Buffer !== 'undefined') { + // for Node.js environment + return Array.from(new Float32Array(Buffer.from(base64Str, 'base64').buffer)); + } else { + // for legacy web platform APIs + const binaryStr = atob(base64Str); + const len = binaryStr.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryStr.charCodeAt(i); + } + return Array.from(new Float32Array(bytes.buffer)); + } +}; + export function isObj(obj: unknown): obj is Record { return obj != null && typeof obj === 'object' && !Array.isArray(obj); } diff --git a/src/resources/embeddings.ts b/src/resources/embeddings.ts index d01ffc807..a4be9ca3c 100644 --- a/src/resources/embeddings.ts +++ b/src/resources/embeddings.ts @@ -9,9 +9,47 @@ export class Embeddings extends APIResource { */ create( body: EmbeddingCreateParams, - options?: Core.RequestOptions, + options?: Core.RequestOptions, ): Core.APIPromise { - return this._client.post('/embeddings', { body, ...options }); + const hasUserProvidedEncodingFormat = !!body.encoding_format; + // No encoding_format specified, defaulting to base64 for performance reasons + // See https://github.com/openai/openai-node/pull/1312 + let encoding_format: EmbeddingCreateParams['encoding_format'] = + hasUserProvidedEncodingFormat ? body.encoding_format : 'base64'; + + if (hasUserProvidedEncodingFormat) { + Core.debug('Request', 'User defined encoding_format:', body.encoding_format); + } + + const response: Core.APIPromise = this._client.post('/embeddings', { + body: { + ...body, + encoding_format: encoding_format as EmbeddingCreateParams['encoding_format'], + }, + ...options, + }); + + // if the user specified an encoding_format, return the response as-is + if (hasUserProvidedEncodingFormat) { + return response; + } + + // in this stage, we are sure the user did not specify an encoding_format + // and we defaulted to base64 for performance reasons + // we are sure then that the response is base64 encoded, let's decode it + // the returned result will be a float32 array since this is OpenAI API's default encoding + Core.debug('response', 'Decoding base64 embeddings to float32 array'); + + return (response as Core.APIPromise)._thenUnwrap((response) => { + if (response && response.data) { + response.data.forEach((embeddingBase64Obj) => { + const embeddingBase64Str = embeddingBase64Obj.embedding as unknown as string; + embeddingBase64Obj.embedding = Core.toFloat32Array(embeddingBase64Str); + }); + } + + return response; + }); } } diff --git a/src/version.ts b/src/version.ts index 03d899bdd..0095d88c8 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '4.90.0'; // x-release-please-version +export const VERSION = '4.91.0'; // x-release-please-version diff --git a/tests/api-resources/embeddings.test.ts b/tests/api-resources/embeddings.test.ts index 46dd1b2a3..e226ade9e 100644 --- a/tests/api-resources/embeddings.test.ts +++ b/tests/api-resources/embeddings.test.ts @@ -32,4 +32,35 @@ describe('resource embeddings', () => { user: 'user-1234', }); }); + + test('create: encoding_format=float should create float32 embeddings', async () => { + const response = await client.embeddings.create({ + input: 'The quick brown fox jumped over the lazy dog', + model: 'text-embedding-3-small', + }); + + expect(response.data?.at(0)?.embedding).toBeInstanceOf(Array); + expect(Number.isFinite(response.data?.at(0)?.embedding.at(0))).toBe(true); + }); + + test('create: encoding_format=base64 should create float32 embeddings', async () => { + const response = await client.embeddings.create({ + input: 'The quick brown fox jumped over the lazy dog', + model: 'text-embedding-3-small', + encoding_format: 'base64', + }); + + expect(response.data?.at(0)?.embedding).toBeInstanceOf(Array); + expect(Number.isFinite(response.data?.at(0)?.embedding.at(0))).toBe(true); + }); + + test('create: encoding_format=default should create float32 embeddings', async () => { + const response = await client.embeddings.create({ + input: 'The quick brown fox jumped over the lazy dog', + model: 'text-embedding-3-small', + }); + + expect(response.data?.at(0)?.embedding).toBeInstanceOf(Array); + expect(Number.isFinite(response.data?.at(0)?.embedding.at(0))).toBe(true); + }); });