feat: add matryoshka embedding truncation#6606
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces Matryoshka embedding truncation support, allowing users to optionally truncate document and query embeddings to a configured dimension. It adds a utility module matryoshkaEmbeddings.ts along with unit tests, integrates this truncation into the document store services, and updates the UI to support configuring the truncation dimensions. The review feedback highlights a potential runtime crash when using Object.create on embedding instances that utilize native JavaScript private fields, suggesting a monkey-patching approach instead. Additionally, it recommends using loose equality (== null) for nullish checks in accordance with standard idioms.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const wrappedEmbeddings = Object.create(embeddings) as T | ||
|
|
||
| wrappedEmbeddings.embedDocuments = async (documents: string[]): Promise<number[][]> => { | ||
| const vectors = await embeddings.embedDocuments(documents) | ||
| return vectors.map((vector) => truncateVector(vector, dimension)) | ||
| } | ||
|
|
||
| wrappedEmbeddings.embedQuery = async (document: string): Promise<number[]> => { | ||
| const vector = await embeddings.embedQuery(document) | ||
| return truncateVector(vector, dimension) | ||
| } | ||
|
|
||
| return wrappedEmbeddings |
There was a problem hiding this comment.
Using Object.create(embeddings) to wrap the embeddings instance can cause runtime crashes if the underlying class uses native JavaScript private fields (e.g., #apiKey). Any non-overridden method called on the prototype-derived wrappedEmbeddings object that accesses these private fields will throw a TypeError: Cannot read private member from an object whose class did not declare it.
Since the embedding instance is created fresh for each operation and is not shared globally, directly monkey-patching the methods on the original embeddings instance is a much safer and robust approach that preserves the this context and private field access.
const originalEmbedDocuments = embeddings.embedDocuments.bind(embeddings)
const originalEmbedQuery = embeddings.embedQuery.bind(embeddings)
embeddings.embedDocuments = async (documents: string[]): Promise<number[][]> => {
const vectors = await originalEmbedDocuments(documents)
return vectors.map((vector) => truncateVector(vector, dimension))
}
embeddings.embedQuery = async (document: string): Promise<number[]> => {
const vector = await originalEmbedQuery(document)
return truncateVector(vector, dimension)
}
return embeddings| export const MATRYOSHKA_TRUNCATE_DIMENSIONS = 'matryoshkaTruncateDimensions' | ||
|
|
||
| const parseTruncateDimension = (value: unknown): number | undefined => { | ||
| if (value === undefined || value === null || value === '') return undefined |
There was a problem hiding this comment.
According to the general rules, loose equality (== null) should be used as a standard idiom for a 'nullish' check that covers both null and undefined.
| if (value === undefined || value === null || value === '') return undefined | |
| if (value == null || value === '') return undefined |
References
- In JavaScript/TypeScript, use loose equality (
== null) as a standard idiom for a 'nullish' check that covers both 'null' and 'undefined'.
Summary
Closes #4361
Tests