import { Embeddings } from "../embeddings/base.js"; import { Document } from "../document.js"; import { BaseRetriever, BaseRetrieverInput } from "../schema/retriever.js"; import { Serializable } from "../load/serializable.js"; import { CallbackManagerForRetrieverRun, Callbacks, } from "../callbacks/manager.js"; /** * Type for options when adding a document to the VectorStore. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any type AddDocumentOptions = Record; /** * Type for options when performing a maximal marginal relevance search. */ export type MaxMarginalRelevanceSearchOptions = { k: number; fetchK?: number; lambda?: number; filter?: FilterType; }; /** * Type for options when performing a maximal marginal relevance search * with the VectorStoreRetriever. */ export type VectorStoreRetrieverMMRSearchKwargs = { fetchK?: number; lambda?: number; }; /** * Type for input when creating a VectorStoreRetriever instance. */ export type VectorStoreRetrieverInput = BaseRetrieverInput & ( | { vectorStore: V; k?: number; filter?: V["FilterType"]; searchType?: "similarity"; } | { vectorStore: V; k?: number; filter?: V["FilterType"]; searchType: "mmr"; searchKwargs?: VectorStoreRetrieverMMRSearchKwargs; } ); /** * Class for performing document retrieval from a VectorStore. Can perform * similarity search or maximal marginal relevance search. */ export class VectorStoreRetriever< V extends VectorStore = VectorStore > extends BaseRetriever { static lc_name() { return "VectorStoreRetriever"; } get lc_namespace() { return ["langchain", "retrievers", "base"]; } vectorStore: V; k = 4; searchType = "similarity"; searchKwargs?: VectorStoreRetrieverMMRSearchKwargs; filter?: V["FilterType"]; _vectorstoreType(): string { return this.vectorStore._vectorstoreType(); } constructor(fields: VectorStoreRetrieverInput) { super(fields); this.vectorStore = fields.vectorStore; this.k = fields.k ?? this.k; this.searchType = fields.searchType ?? this.searchType; this.filter = fields.filter; if (fields.searchType === "mmr") { this.searchKwargs = fields.searchKwargs; } } async _getRelevantDocuments( query: string, runManager?: CallbackManagerForRetrieverRun ): Promise { if (this.searchType === "mmr") { if (typeof this.vectorStore.maxMarginalRelevanceSearch !== "function") { throw new Error( `The vector store backing this retriever, ${this._vectorstoreType()} does not support max marginal relevance search.` ); } return this.vectorStore.maxMarginalRelevanceSearch( query, { k: this.k, filter: this.filter, ...this.searchKwargs, }, runManager?.getChild("vectorstore") ); } return this.vectorStore.similaritySearch( query, this.k, this.filter, runManager?.getChild("vectorstore") ); } async addDocuments( documents: Document[], options?: AddDocumentOptions ): Promise { return this.vectorStore.addDocuments(documents, options); } } /** * Abstract class representing a store of vectors. Provides methods for * adding vectors and documents, deleting from the store, and searching * the store. */ export abstract class VectorStore extends Serializable { declare FilterType: object | string; lc_namespace = ["langchain", "vectorstores", this._vectorstoreType()]; embeddings: Embeddings; // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(embeddings: Embeddings, dbConfig: Record) { super(dbConfig); this.embeddings = embeddings; } abstract _vectorstoreType(): string; abstract addVectors( vectors: number[][], documents: Document[], options?: AddDocumentOptions ): Promise; abstract addDocuments( documents: Document[], options?: AddDocumentOptions ): Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any async delete(_params?: Record): Promise { throw new Error("Not implemented."); } abstract similaritySearchVectorWithScore( query: number[], k: number, filter?: this["FilterType"] ): Promise<[Document, number][]>; async similaritySearch( query: string, k = 4, filter: this["FilterType"] | undefined = undefined, _callbacks: Callbacks | undefined = undefined // implement passing to embedQuery later ): Promise { const results = await this.similaritySearchVectorWithScore( await this.embeddings.embedQuery(query), k, filter ); return results.map((result) => result[0]); } async similaritySearchWithScore( query: string, k = 4, filter: this["FilterType"] | undefined = undefined, _callbacks: Callbacks | undefined = undefined // implement passing to embedQuery later ): Promise<[Document, number][]> { return this.similaritySearchVectorWithScore( await this.embeddings.embedQuery(query), k, filter ); } /** * Return documents selected using the maximal marginal relevance. * Maximal marginal relevance optimizes for similarity to the query AND diversity * among selected documents. * * @param {string} query - Text to look up documents similar to. * @param {number} options.k - Number of documents to return. * @param {number} options.fetchK - Number of documents to fetch before passing to the MMR algorithm. * @param {number} options.lambda - Number between 0 and 1 that determines the degree of diversity among the results, * where 0 corresponds to maximum diversity and 1 to minimum diversity. * @param {this["FilterType"]} options.filter - Optional filter * @param _callbacks * * @returns {Promise} - List of documents selected by maximal marginal relevance. */ async maxMarginalRelevanceSearch?( query: string, options: MaxMarginalRelevanceSearchOptions, _callbacks: Callbacks | undefined // implement passing to embedQuery later ): Promise; static fromTexts( _texts: string[], _metadatas: object[] | object, _embeddings: Embeddings, // eslint-disable-next-line @typescript-eslint/no-explicit-any _dbConfig: Record ): Promise { throw new Error( "the Langchain vectorstore implementation you are using forgot to override this, please report a bug" ); } static fromDocuments( _docs: Document[], _embeddings: Embeddings, // eslint-disable-next-line @typescript-eslint/no-explicit-any _dbConfig: Record ): Promise { throw new Error( "the Langchain vectorstore implementation you are using forgot to override this, please report a bug" ); } asRetriever( kOrFields?: number | Partial>, filter?: this["FilterType"], callbacks?: Callbacks, tags?: string[], metadata?: Record, verbose?: boolean ): VectorStoreRetriever { if (typeof kOrFields === "number") { return new VectorStoreRetriever({ vectorStore: this, k: kOrFields, filter, tags: [...(tags ?? []), this._vectorstoreType()], metadata, verbose, callbacks, }); } else { const params = { vectorStore: this, k: kOrFields?.k, filter: kOrFields?.filter, tags: [...(kOrFields?.tags ?? []), this._vectorstoreType()], metadata: kOrFields?.metadata, verbose: kOrFields?.verbose, callbacks: kOrFields?.callbacks, searchType: kOrFields?.searchType, }; if (kOrFields?.searchType === "mmr") { return new VectorStoreRetriever({ ...params, searchKwargs: kOrFields.searchKwargs, }); } return new VectorStoreRetriever({ ...params }); } } } /** * Abstract class extending VectorStore with functionality for saving and * loading the vector store. */ export abstract class SaveableVectorStore extends VectorStore { abstract save(directory: string): Promise; static load( _directory: string, _embeddings: Embeddings ): Promise { throw new Error("Not implemented"); } }