Skip to content

Commit

Permalink
perf(embedding): always run embedding creation as base64
Browse files Browse the repository at this point in the history
Requesting base64 encoded embeddings returns smaller body sizes, on average ~60% smaller than float32 encoded. In other words, the size of the response body containing embeddings in float32 is ~2.3x bigger than base64 encoded embedding.

We always request embedding creating encoded as base64, and then decoded them to float32 based on the user's provided encoding_format parameter.

Closes openai#1310
  • Loading branch information
manekinekko committed Feb 8, 2025
1 parent 41a7ce3 commit 7702d54
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 2 deletions.
41 changes: 39 additions & 2 deletions src/resources/embeddings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,46 @@ export class Embeddings extends APIResource {
*/
create(
body: EmbeddingCreateParams,
options?: Core.RequestOptions,
options?: Core.RequestOptions<EmbeddingCreateParams>,
): Core.APIPromise<CreateEmbeddingResponse> {
return this._client.post('/embeddings', { body, ...options });
const userInitialEncodingFormat = body.encoding_format;
Core.debug('request', 'Sending request with arguments:', { body, ...options });

const base64Response = this._client.post<EmbeddingCreateParams, CreateEmbeddingResponse>('/embeddings', {
body: {
...body,
// Force base64 encoding for vector embeddings creation
// See https://github.com/openai/openai-node/issues/1310
encoding_format: 'base64',
},
...options,
});

if (userInitialEncodingFormat === 'base64') {
// if the user requested base64 encoding_format, return the response as-is
return base64Response;
} else {
// we decode the base64 embeddings to float32 array if:
// 1- the user requested 'float' encoding_format,
// 2- the user did not specify an encoding_format (which defaults to 'float') in order to keep backwards compatibility
Core.debug('response', `User requested encoding_format=${userInitialEncodingFormat || 'default'}`);
Core.debug('response', 'Decoding base64 embeddings to float32 array');

return base64Response._thenUnwrap((response) => {
if (response && response.data) {
response.data.forEach((embeddingBase64Obj) => {
console.log(embeddingBase64Obj);
const embeddingBase64Str = embeddingBase64Obj.embedding as unknown as string;
embeddingBase64Obj.embedding = Array.from(
new Float32Array(Buffer.from(embeddingBase64Str, 'base64').buffer),
);
});
Core.debug('response', 'Decoded embeddings:', response.data);
}

return response;
});
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions tests/api-resources/embeddings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,16 @@ describe('resource embeddings', () => {
user: 'user-1234',
});
});

test('create: encoding_format=default should create float32 embeddings', async () => {
const responsePromise = client.embeddings.create({
input: 'The quick brown fox jumped over the lazy dog',
model: 'text-embedding-3-small',
});
const response = await responsePromise;
console.log(response.data?.at(0)?.embedding);

expect(response.data?.at(0)?.embedding).toBeInstanceOf(Array);
expect(Number.isFinite(response.data?.at(0)?.embedding.at(0))).toBe(true);
});
});

0 comments on commit 7702d54

Please sign in to comment.