154 lines
6.6 KiB
JavaScript
154 lines
6.6 KiB
JavaScript
import { VectorStore } from "@langchain/core/vectorstores";
|
|
import { Document } from "@langchain/core/documents";
|
|
/**
|
|
* Class that extends `VectorStore`. It allows to perform similarity search using
|
|
* Voi similarity search engine. The class requires passing Voy Client as an input parameter.
|
|
*/
|
|
export class VoyVectorStore extends VectorStore {
|
|
_vectorstoreType() {
|
|
return "voi";
|
|
}
|
|
constructor(client, embeddings) {
|
|
super(embeddings, {});
|
|
Object.defineProperty(this, "client", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
value: void 0
|
|
});
|
|
Object.defineProperty(this, "numDimensions", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
value: null
|
|
});
|
|
Object.defineProperty(this, "docstore", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
value: []
|
|
});
|
|
this.client = client;
|
|
this.embeddings = embeddings;
|
|
}
|
|
/**
|
|
* Adds documents to the Voy database. The documents are embedded using embeddings provided while instantiating the class.
|
|
* @param documents An array of `Document` instances associated with the vectors.
|
|
*/
|
|
async addDocuments(documents) {
|
|
const texts = documents.map(({ pageContent }) => pageContent);
|
|
if (documents.length === 0) {
|
|
return;
|
|
}
|
|
const firstVector = (await this.embeddings.embedDocuments(texts.slice(0, 1)))[0];
|
|
if (this.numDimensions === null) {
|
|
this.numDimensions = firstVector.length;
|
|
}
|
|
else if (this.numDimensions !== firstVector.length) {
|
|
throw new Error(`Vectors must have the same length as the number of dimensions (${this.numDimensions})`);
|
|
}
|
|
const restResults = await this.embeddings.embedDocuments(texts.slice(1));
|
|
await this.addVectors([firstVector, ...restResults], documents);
|
|
}
|
|
/**
|
|
* Adds vectors to the Voy database. The vectors are associated with
|
|
* the provided documents.
|
|
* @param vectors An array of vectors to be added to the database.
|
|
* @param documents An array of `Document` instances associated with the vectors.
|
|
*/
|
|
async addVectors(vectors, documents) {
|
|
if (vectors.length === 0) {
|
|
return;
|
|
}
|
|
if (this.numDimensions === null) {
|
|
this.numDimensions = vectors[0].length;
|
|
}
|
|
if (vectors.length !== documents.length) {
|
|
throw new Error(`Vectors and metadata must have the same length`);
|
|
}
|
|
if (!vectors.every((v) => v.length === this.numDimensions)) {
|
|
throw new Error(`Vectors must have the same length as the number of dimensions (${this.numDimensions})`);
|
|
}
|
|
vectors.forEach((item, idx) => {
|
|
const doc = documents[idx];
|
|
this.docstore.push({ embeddings: item, document: doc });
|
|
});
|
|
const embeddings = this.docstore.map((item, idx) => ({
|
|
id: String(idx),
|
|
embeddings: item.embeddings,
|
|
title: "",
|
|
url: "",
|
|
}));
|
|
this.client.index({ embeddings });
|
|
}
|
|
/**
|
|
* Searches for vectors in the Voy database that are similar to the
|
|
* provided query vector.
|
|
* @param query The query vector.
|
|
* @param k The number of similar vectors to return.
|
|
* @returns A promise that resolves with an array of tuples, each containing a `Document` instance and a similarity score.
|
|
*/
|
|
async similaritySearchVectorWithScore(query, k) {
|
|
if (this.numDimensions === null) {
|
|
throw new Error("There aren't any elements in the index yet.");
|
|
}
|
|
if (query.length !== this.numDimensions) {
|
|
throw new Error(`Query vector must have the same length as the number of dimensions (${this.numDimensions})`);
|
|
}
|
|
const itemsToQuery = Math.min(this.docstore.length, k);
|
|
if (itemsToQuery > this.docstore.length) {
|
|
console.warn(`k (${k}) is greater than the number of elements in the index (${this.docstore.length}), setting k to ${itemsToQuery}`);
|
|
}
|
|
const results = this.client.search(new Float32Array(query), itemsToQuery);
|
|
return results.neighbors.map(({ id }, idx) => [this.docstore[parseInt(id, 10)].document, idx]);
|
|
}
|
|
/**
|
|
* Method to delete data from the Voy index. It can delete data based
|
|
* on specific IDs or a filter.
|
|
* @param params Object that includes either an array of IDs or a filter for the data to be deleted.
|
|
* @returns Promise that resolves when the deletion is complete.
|
|
*/
|
|
async delete(params) {
|
|
if (params.deleteAll === true) {
|
|
await this.client.clear();
|
|
}
|
|
else {
|
|
throw new Error(`You must provide a "deleteAll" parameter.`);
|
|
}
|
|
}
|
|
/**
|
|
* Creates a new `VoyVectorStore` instance from an array of text strings. The text
|
|
* strings are converted to `Document` instances and added to the Voy
|
|
* database.
|
|
* @param texts An array of text strings.
|
|
* @param metadatas An array of metadata objects or a single metadata object. If an array is provided, it must have the same length as the `texts` array.
|
|
* @param embeddings An `Embeddings` instance used to generate embeddings for the documents.
|
|
* @param client An instance of Voy client to use in the underlying operations.
|
|
* @returns A promise that resolves with a new `VoyVectorStore` instance.
|
|
*/
|
|
static async fromTexts(texts, metadatas, embeddings, client) {
|
|
const docs = [];
|
|
for (let i = 0; i < texts.length; i += 1) {
|
|
const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;
|
|
const newDoc = new Document({
|
|
pageContent: texts[i],
|
|
metadata,
|
|
});
|
|
docs.push(newDoc);
|
|
}
|
|
return VoyVectorStore.fromDocuments(docs, embeddings, client);
|
|
}
|
|
/**
|
|
* Creates a new `VoyVectorStore` instance from an array of `Document` instances.
|
|
* The documents are added to the Voy database.
|
|
* @param docs An array of `Document` instances.
|
|
* @param embeddings An `Embeddings` instance used to generate embeddings for the documents.
|
|
* @param client An instance of Voy client to use in the underlying operations.
|
|
* @returns A promise that resolves with a new `VoyVectorStore` instance.
|
|
*/
|
|
static async fromDocuments(docs, embeddings, client) {
|
|
const instance = new VoyVectorStore(client, embeddings);
|
|
await instance.addDocuments(docs);
|
|
return instance;
|
|
}
|
|
}
|