UNPKG

@langchain/community

Version:
281 lines (279 loc) • 11.1 kB
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs'); const require_utils_cassandra = require('../utils/cassandra.cjs'); const __langchain_core_documents = require_rolldown_runtime.__toESM(require("@langchain/core/documents")); const uuid = require_rolldown_runtime.__toESM(require("uuid")); const __langchain_core_vectorstores = require_rolldown_runtime.__toESM(require("@langchain/core/vectorstores")); const __langchain_core_utils_math = require_rolldown_runtime.__toESM(require("@langchain/core/utils/math")); //#region src/vectorstores/cassandra.ts var cassandra_exports = {}; require_rolldown_runtime.__export(cassandra_exports, { CassandraStore: () => CassandraStore }); /** * Class for interacting with the Cassandra database. It extends the * VectorStore class and provides methods for adding vectors and * documents, searching for similar vectors, and creating instances from * texts or documents. */ var CassandraStore = class CassandraStore extends __langchain_core_vectorstores.VectorStore { table; idColumnAutoName = "id"; idColumnAutoGenerated; vectorColumnName = "vector"; vectorColumn; textColumnName = "text"; textColumn; metadataColumnDefaultName = "metadata"; metadataColumns; similarityColumn; embeddingColumnAlias = "embedding"; _vectorstoreType() { return "cassandra"; } _cleanArgs(args) { const { table, dimensions, primaryKey, nonKeyColumns, indices, metadataColumns, vectorType = "cosine" } = args; if (!table || !dimensions) throw new Error("Missing required arguments"); function _toArray(value) { return Array.isArray(value) ? value : [value]; } const indicesArg = indices || []; let primaryKeyArg; if (primaryKey) primaryKeyArg = _toArray(primaryKey); else primaryKeyArg = [{ name: this.idColumnAutoName, type: "uuid", partition: true }]; const combinedColumns = [..._toArray(nonKeyColumns || []), ..._toArray(metadataColumns || [])]; const deduplicatedColumns = combinedColumns.filter((col, index, self) => self.findIndex((c) => c.name === col.name) === index); const nonKeyColumnsArg = [...deduplicatedColumns]; if (nonKeyColumnsArg.length === 0) { nonKeyColumnsArg.push({ name: this.metadataColumnDefaultName, type: "map<text, text>" }); indicesArg.push({ name: `idx_${this.metadataColumnDefaultName}_${table}_keys`, value: `(keys(${this.metadataColumnDefaultName}))` }); indicesArg.push({ name: `idx_${this.metadataColumnDefaultName}_${table}_entries`, value: `(entries(${this.metadataColumnDefaultName}))` }); } const addDefaultNonKeyColumnIfNeeded = (defaultColumn) => { const column = nonKeyColumnsArg.find((col) => col.name === defaultColumn.name); if (!column) nonKeyColumnsArg.push(defaultColumn); }; addDefaultNonKeyColumnIfNeeded({ name: this.textColumnName, type: "text" }); addDefaultNonKeyColumnIfNeeded({ name: this.vectorColumnName, type: `VECTOR<FLOAT,${dimensions}>`, alias: this.embeddingColumnAlias }); if (!indicesArg.some((index) => (/* @__PURE__ */ new RegExp(`\\(\\s*${this.vectorColumnName.toLowerCase()}\\s*\\)`)).test(index.value.toLowerCase()))) indicesArg.push({ name: `idx_${this.vectorColumnName}_${table}`, value: `(${this.vectorColumnName})`, options: `{'similarity_function': '${vectorType.toLowerCase()}'}` }); const metadataColumnsArg = [...primaryKeyArg, ...nonKeyColumnsArg].filter((column) => column.name !== this.vectorColumnName && column.name !== this.textColumnName); return { ...args, vectorType, primaryKey: primaryKeyArg, nonKeyColumns: nonKeyColumnsArg, metadataColumns: metadataColumnsArg, indices: indicesArg }; } _getColumnByName(columns, columnName) { const columnsArray = Array.isArray(columns) ? columns : [columns]; const column = columnsArray.find((col) => col.name === columnName); if (!column) throw new Error(`Column ${columnName} not found`); return column; } constructor(embeddings, args) { super(embeddings, args); const cleanedArgs = this._cleanArgs(args); if (!cleanedArgs.nonKeyColumns || cleanedArgs.nonKeyColumns.length === 0) throw new Error("No non-key columns provided"); this.vectorColumn = this._getColumnByName(cleanedArgs.nonKeyColumns, this.vectorColumnName); this.textColumn = this._getColumnByName(cleanedArgs.nonKeyColumns, this.textColumnName); this.similarityColumn = { name: `similarity_${cleanedArgs.vectorType}(${this.vectorColumnName},?)`, alias: "similarity_score", type: "" }; this.idColumnAutoGenerated = !args.primaryKey; this.metadataColumns = cleanedArgs.metadataColumns; this.table = new require_utils_cassandra.CassandraTable(cleanedArgs); } /** * Method to save vectors to the Cassandra database. * @param vectors Vectors to save. * @param documents The documents associated with the vectors. * @returns Promise that resolves when the vectors have been added. */ async addVectors(vectors, documents) { if (vectors.length === 0) return; const values = vectors.map((vector, index) => { const document = documents[index]; const docMetadata = document.metadata || {}; if (this.idColumnAutoGenerated && (docMetadata[this.idColumnAutoName] === void 0 || docMetadata[this.idColumnAutoName] === "")) docMetadata[this.idColumnAutoName] = (0, uuid.v4)(); const row = []; this.metadataColumns.forEach((col) => { row.push(docMetadata[col.name] || null); }); row.push(document.pageContent); row.push(new Float32Array(vector)); return row; }); const columns = [ ...this.metadataColumns, { name: this.textColumnName, type: "" }, { name: this.vectorColumnName, type: "" } ]; return this.table.upsert(values, columns); } getCassandraTable() { return this.table; } /** * Method to add documents to the Cassandra database. * @param documents The documents to add. * @returns Promise that resolves when the documents have been added. */ async addDocuments(documents) { return this.addVectors(await this.embeddings.embedDocuments(documents.map((d) => d.pageContent)), documents); } /** * Helper method to search for vectors that are similar to a given query vector. * @param query The query vector. * @param k The number of similar Documents to return. * @param filter Optional filter to be applied as a WHERE clause. * @param includeEmbedding Whether to include the embedding vectors in the results. * @returns Promise that resolves with an array of tuples, each containing a Document and a score. */ async search(query, k, filter, includeEmbedding) { const vectorAsFloat32Array = new Float32Array(query); const similarityColumnWithBinds = { ...this.similarityColumn, binds: [vectorAsFloat32Array] }; const queryCols = [ ...this.metadataColumns, this.textColumn, similarityColumnWithBinds ]; if (includeEmbedding) queryCols.push(this.vectorColumn); const orderBy = { name: this.vectorColumnName, operator: "ANN OF", value: [vectorAsFloat32Array] }; const queryResultSet = await this.table.select(queryCols, filter, [orderBy], k); return queryResultSet?.rows.map((row) => { const textContent = row[this.textColumnName]; const sanitizedRow = { ...row }; delete sanitizedRow[this.textColumnName]; delete sanitizedRow.similarity_score; Object.keys(sanitizedRow).forEach((key) => { if (sanitizedRow[key] === null) delete sanitizedRow[key]; }); return [new __langchain_core_documents.Document({ pageContent: textContent, metadata: sanitizedRow }), row.similarity_score]; }); } /** * Method to search for vectors that are similar to a given query vector. * @param query The query vector. * @param k The number of similar Documents to return. * @param filter Optional filter to be applied as a WHERE clause. * @returns Promise that resolves with an array of tuples, each containing a Document and a score. */ async similaritySearchVectorWithScore(query, k, filter) { return this.search(query, k, filter, false); } /** * Method to search for vectors that are similar to a given query vector, but with * the results selected using the maximal marginal relevance. * @param query The query string. * @param options.k The number of similar Documents to return. * @param options.fetchK=4*k The number of records to fetch before passing to the MMR algorithm. * @param options.lambda=0.5 The degree of diversity among the results between 0 (maximum diversity) and 1 (minimum diversity). * @param options.filter Optional filter to be applied as a WHERE clause. * @returns List of documents selected by maximal marginal relevance. */ async maxMarginalRelevanceSearch(query, options) { const { k, fetchK = 4 * k, lambda = .5, filter } = options; const queryEmbedding = await this.embeddings.embedQuery(query); const queryResults = await this.search(queryEmbedding, fetchK, filter, true); const embeddingList = queryResults.map((doc) => doc[0].metadata[this.embeddingColumnAlias]); const mmrIndexes = (0, __langchain_core_utils_math.maximalMarginalRelevance)(queryEmbedding, embeddingList, lambda, k); return mmrIndexes.map((idx) => { const doc = queryResults[idx][0]; delete doc.metadata[this.embeddingColumnAlias]; return doc; }); } /** * Static method to create an instance of CassandraStore from texts. * @param texts The texts to use. * @param metadatas The metadata associated with the texts. * @param embeddings The embeddings to use. * @param args The arguments for the CassandraStore. * @returns Promise that resolves with a new instance of CassandraStore. */ static async fromTexts(texts, metadatas, embeddings, args) { const docs = []; for (let index = 0; index < texts.length; index += 1) { const metadata = Array.isArray(metadatas) ? metadatas[index] : metadatas; const doc = new __langchain_core_documents.Document({ pageContent: texts[index], metadata }); docs.push(doc); } return CassandraStore.fromDocuments(docs, embeddings, args); } /** * Static method to create an instance of CassandraStore from documents. * @param docs The documents to use. * @param embeddings The embeddings to use. * @param args The arguments for the CassandraStore. * @returns Promise that resolves with a new instance of CassandraStore. */ static async fromDocuments(docs, embeddings, args) { const instance = new this(embeddings, args); await instance.addDocuments(docs); return instance; } /** * Static method to create an instance of CassandraStore from an existing * index. * @param embeddings The embeddings to use. * @param args The arguments for the CassandraStore. * @returns Promise that resolves with a new instance of CassandraStore. */ static async fromExistingIndex(embeddings, args) { const instance = new this(embeddings, args); return instance; } }; //#endregion exports.CassandraStore = CassandraStore; Object.defineProperty(exports, 'cassandra_exports', { enumerable: true, get: function () { return cassandra_exports; } }); //# sourceMappingURL=cassandra.cjs.map