@langchain/community
Version:
Third-party integrations for LangChain.js
1 lines • 17.9 kB
Source Map (JSON)
{"version":3,"file":"supabase.cjs","names":["VectorStore","embeddings: EmbeddingsInterface","args: SupabaseLibArgs","documents: Document[]","options?: { ids?: string[] | number[] }","vectors: number[][]","returnedIds: string[]","params: { ids: string[] | number[] }","query: number[]","k: number","filter?: this[\"FilterType\"]","matchDocumentsParams: Partial<SearchEmbeddingsParams>","filterFunction: SupabaseFilterRPCCall","rpcCall","result: [Document, number][]","Document","query: string","options: MaxMarginalRelevanceSearchOptions<this[\"FilterType\"]>","texts: string[]","metadatas: object[] | object","dbConfig: SupabaseLibArgs","docs: Document[]"],"sources":["../../src/vectorstores/supabase.ts"],"sourcesContent":["import type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { PostgrestFilterBuilder } from \"@supabase/postgrest-js\";\nimport {\n MaxMarginalRelevanceSearchOptions,\n VectorStore,\n} from \"@langchain/core/vectorstores\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { Document } from \"@langchain/core/documents\";\nimport { maximalMarginalRelevance } from \"@langchain/core/utils/math\";\n\n/**\n * Interface for the parameters required for searching embeddings.\n */\ninterface SearchEmbeddingsParams {\n query_embedding: number[];\n match_count: number; // int\n filter?: SupabaseMetadata | SupabaseFilterRPCCall;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type SupabaseMetadata = Record<string, any>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type SupabaseFilter = PostgrestFilterBuilder<any, any, any>;\nexport type SupabaseFilterRPCCall = (rpcCall: SupabaseFilter) => SupabaseFilter;\n\n/**\n * Interface for the response returned when searching embeddings.\n */\ninterface SearchEmbeddingsResponse {\n id: number;\n content: string;\n metadata: object;\n embedding: number[];\n similarity: number;\n}\n\n/**\n * Interface for the arguments required to initialize a Supabase library.\n */\nexport interface SupabaseLibArgs {\n client: SupabaseClient;\n tableName?: string;\n queryName?: string;\n filter?: SupabaseMetadata | SupabaseFilterRPCCall;\n upsertBatchSize?: number;\n}\n\n/**\n * Supabase vector store integration.\n *\n * Setup:\n * Install `@langchain/community` and `@supabase/supabase-js`.\n *\n * ```bash\n * npm install @langchain/community @supabase/supabase-js\n * ```\n *\n * See https://js.langchain.com/docs/integrations/vectorstores/supabase for\n * instructions on how to set up your Supabase instance.\n *\n * ## [Constructor args](https://api.js.langchain.com/classes/_langchain_community.vectorstores_supabase.SupabaseVectorStore.html#constructor)\n *\n * <details open>\n * <summary><strong>Instantiate</strong></summary>\n *\n * ```typescript\n * import { SupabaseVectorStore } from \"@langchain/community/vectorstores/supabase\";\n * import { OpenAIEmbeddings } from \"@langchain/openai\";\n *\n * import { createClient } from \"@supabase/supabase-js\";\n *\n * const embeddings = new OpenAIEmbeddings({\n * model: \"text-embedding-3-small\",\n * });\n *\n * const supabaseClient = createClient(\n * process.env.SUPABASE_URL,\n * process.env.SUPABASE_PRIVATE_KEY\n * );\n *\n * const vectorStore = new SupabaseVectorStore(embeddings, {\n * client: supabaseClient,\n * tableName: \"documents\",\n * queryName: \"match_documents\",\n * });\n * ```\n * </details>\n *\n * <br />\n *\n * <details>\n * <summary><strong>Add documents</strong></summary>\n *\n * ```typescript\n * import type { Document } from '@langchain/core/documents';\n *\n * const document1 = { pageContent: \"foo\", metadata: { baz: \"bar\" } };\n * const document2 = { pageContent: \"thud\", metadata: { bar: \"baz\" } };\n * const document3 = { pageContent: \"i will be deleted :(\", metadata: {} };\n *\n * const documents: Document[] = [document1, document2, document3];\n * const ids = [\"1\", \"2\", \"3\"];\n * await vectorStore.addDocuments(documents, { ids });\n * ```\n * </details>\n *\n * <br />\n *\n * <details>\n * <summary><strong>Delete documents</strong></summary>\n *\n * ```typescript\n * await vectorStore.delete({ ids: [\"3\"] });\n * ```\n * </details>\n *\n * <br />\n *\n * <details>\n * <summary><strong>Similarity search</strong></summary>\n *\n * ```typescript\n * const results = await vectorStore.similaritySearch(\"thud\", 1);\n * for (const doc of results) {\n * console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n * }\n * // Output: * thud [{\"baz\":\"bar\"}]\n * ```\n * </details>\n *\n * <br />\n *\n *\n * <details>\n * <summary><strong>Similarity search with filter</strong></summary>\n *\n * ```typescript\n * const resultsWithFilter = await vectorStore.similaritySearch(\"thud\", 1, { baz: \"bar\" });\n *\n * for (const doc of resultsWithFilter) {\n * console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n * }\n * // Output: * foo [{\"baz\":\"bar\"}]\n * ```\n * </details>\n *\n * <br />\n *\n *\n * <details>\n * <summary><strong>Similarity search with score</strong></summary>\n *\n * ```typescript\n * const resultsWithScore = await vectorStore.similaritySearchWithScore(\"qux\", 1);\n * for (const [doc, score] of resultsWithScore) {\n * console.log(`* [SIM=${score.toFixed(6)}] ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n * }\n * // Output: * [SIM=0.000000] qux [{\"bar\":\"baz\",\"baz\":\"bar\"}]\n * ```\n * </details>\n *\n * <br />\n *\n * <details>\n * <summary><strong>As a retriever</strong></summary>\n *\n * ```typescript\n * const retriever = vectorStore.asRetriever({\n * searchType: \"mmr\", // Leave blank for standard similarity search\n * k: 1,\n * });\n * const resultAsRetriever = await retriever.invoke(\"thud\");\n * console.log(resultAsRetriever);\n *\n * // Output: [Document({ metadata: { \"baz\":\"bar\" }, pageContent: \"thud\" })]\n * ```\n * </details>\n *\n * <br />\n */\nexport class SupabaseVectorStore extends VectorStore {\n declare FilterType: SupabaseMetadata | SupabaseFilterRPCCall;\n\n client: SupabaseClient;\n\n tableName: string;\n\n queryName: string;\n\n filter?: SupabaseMetadata | SupabaseFilterRPCCall;\n\n upsertBatchSize = 500;\n\n _vectorstoreType(): string {\n return \"supabase\";\n }\n\n constructor(embeddings: EmbeddingsInterface, args: SupabaseLibArgs) {\n super(embeddings, args);\n\n this.client = args.client;\n this.tableName = args.tableName || \"documents\";\n this.queryName = args.queryName || \"match_documents\";\n this.filter = args.filter;\n this.upsertBatchSize = args.upsertBatchSize ?? this.upsertBatchSize;\n }\n\n /**\n * Adds documents to the vector store.\n * @param documents The documents to add.\n * @param options Optional parameters for adding the documents.\n * @returns A promise that resolves when the documents have been added.\n */\n async addDocuments(\n documents: Document[],\n options?: { ids?: string[] | number[] }\n ) {\n const texts = documents.map(({ pageContent }) => pageContent);\n return this.addVectors(\n await this.embeddings.embedDocuments(texts),\n documents,\n options\n );\n }\n\n /**\n * Adds vectors to the vector store.\n * @param vectors The vectors to add.\n * @param documents The documents associated with the vectors.\n * @param options Optional parameters for adding the vectors.\n * @returns A promise that resolves with the IDs of the added vectors when the vectors have been added.\n */\n async addVectors(\n vectors: number[][],\n documents: Document[],\n options?: { ids?: string[] | number[] }\n ) {\n const rows = vectors.map((embedding, idx) => ({\n content: documents[idx].pageContent,\n embedding,\n metadata: documents[idx].metadata,\n }));\n\n // upsert returns 500/502/504 (yes really any of them) if given too many rows/characters\n // ~2000 trips it, but my data is probably smaller than average pageContent and metadata\n let returnedIds: string[] = [];\n for (let i = 0; i < rows.length; i += this.upsertBatchSize) {\n const chunk = rows.slice(i, i + this.upsertBatchSize).map((row, j) => {\n if (options?.ids) {\n return { id: options.ids[i + j], ...row };\n }\n return row;\n });\n\n const res = await this.client.from(this.tableName).upsert(chunk).select();\n if (res.error) {\n throw new Error(\n `Error inserting: ${res.error.message} ${res.status} ${res.statusText}`\n );\n }\n if (res.data) {\n returnedIds = returnedIds.concat(res.data.map((row) => row.id));\n }\n }\n return returnedIds;\n }\n\n /**\n * Deletes vectors from the vector store.\n * @param params The parameters for deleting vectors.\n * @returns A promise that resolves when the vectors have been deleted.\n */\n async delete(params: { ids: string[] | number[] }): Promise<void> {\n const { ids } = params;\n for (const id of ids) {\n await this.client.from(this.tableName).delete().eq(\"id\", id);\n }\n }\n\n protected async _searchSupabase(\n query: number[],\n k: number,\n filter?: this[\"FilterType\"]\n ): Promise<SearchEmbeddingsResponse[]> {\n if (filter && this.filter) {\n throw new Error(\"cannot provide both `filter` and `this.filter`\");\n }\n const _filter = filter ?? this.filter ?? {};\n const matchDocumentsParams: Partial<SearchEmbeddingsParams> = {\n query_embedding: query,\n };\n\n let filterFunction: SupabaseFilterRPCCall;\n\n if (typeof _filter === \"function\") {\n filterFunction = (rpcCall) => _filter(rpcCall).limit(k);\n } else if (typeof _filter === \"object\") {\n matchDocumentsParams.filter = _filter;\n matchDocumentsParams.match_count = k;\n filterFunction = (rpcCall) => rpcCall;\n } else {\n throw new Error(\"invalid filter type\");\n }\n\n const rpcCall = this.client.rpc(this.queryName, matchDocumentsParams);\n\n const { data: searches, error } = await filterFunction(rpcCall);\n\n if (error) {\n throw new Error(\n `Error searching for documents: ${error.code} ${error.message} ${error.details}`\n );\n }\n\n return searches;\n }\n\n /**\n * Performs a similarity search on the vector store.\n * @param query The query vector.\n * @param k The number of results to return.\n * @param filter Optional filter to apply to the search.\n * @returns A promise that resolves with the search results when the search is complete.\n */\n async similaritySearchVectorWithScore(\n query: number[],\n k: number,\n filter?: this[\"FilterType\"]\n ): Promise<[Document, number][]> {\n const searches = await this._searchSupabase(query, k, filter);\n const result: [Document, number][] = searches.map((resp) => [\n new Document({\n metadata: resp.metadata,\n pageContent: resp.content,\n }),\n resp.similarity,\n ]);\n\n return result;\n }\n\n /**\n * Return documents selected using the maximal marginal relevance.\n * Maximal marginal relevance optimizes for similarity to the query AND diversity\n * among selected documents.\n *\n * @param {string} query - Text to look up documents similar to.\n * @param {number} options.k - Number of documents to return.\n * @param {number} options.fetchK=20- Number of documents to fetch before passing to the MMR algorithm.\n * @param {number} options.lambda=0.5 - Number between 0 and 1 that determines the degree of diversity among the results,\n * where 0 corresponds to maximum diversity and 1 to minimum diversity.\n * @param {SupabaseLibArgs} options.filter - Optional filter to apply to the search.\n *\n * @returns {Promise<Document[]>} - List of documents selected by maximal marginal relevance.\n */\n async maxMarginalRelevanceSearch(\n query: string,\n options: MaxMarginalRelevanceSearchOptions<this[\"FilterType\"]>\n ): Promise<Document[]> {\n const queryEmbedding = await this.embeddings.embedQuery(query);\n\n const searches = await this._searchSupabase(\n queryEmbedding,\n options.fetchK ?? 20,\n options.filter\n );\n\n const embeddingList = searches.map((searchResp) => searchResp.embedding);\n\n const mmrIndexes = maximalMarginalRelevance(\n queryEmbedding,\n embeddingList,\n options.lambda,\n options.k\n );\n\n return mmrIndexes.map(\n (idx) =>\n new Document({\n metadata: searches[idx].metadata,\n pageContent: searches[idx].content,\n })\n );\n }\n\n /**\n * Creates a new SupabaseVectorStore instance from an array of texts.\n * @param texts The texts to create documents from.\n * @param metadatas The metadata for the documents.\n * @param embeddings The embeddings to use.\n * @param dbConfig The configuration for the Supabase database.\n * @returns A promise that resolves with a new SupabaseVectorStore instance when the instance has been created.\n */\n static async fromTexts(\n texts: string[],\n metadatas: object[] | object,\n embeddings: EmbeddingsInterface,\n dbConfig: SupabaseLibArgs\n ): Promise<SupabaseVectorStore> {\n const docs: Document[] = [];\n for (let i = 0; i < texts.length; i += 1) {\n const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;\n const newDoc = new Document({\n pageContent: texts[i],\n metadata,\n });\n docs.push(newDoc);\n }\n return SupabaseVectorStore.fromDocuments(docs, embeddings, dbConfig);\n }\n\n /**\n * Creates a new SupabaseVectorStore instance from an array of documents.\n * @param docs The documents to create the instance from.\n * @param embeddings The embeddings to use.\n * @param dbConfig The configuration for the Supabase database.\n * @returns A promise that resolves with a new SupabaseVectorStore instance when the instance has been created.\n */\n static async fromDocuments(\n docs: Document[],\n embeddings: EmbeddingsInterface,\n dbConfig: SupabaseLibArgs\n ): Promise<SupabaseVectorStore> {\n const instance = new this(embeddings, dbConfig);\n await instance.addDocuments(docs);\n return instance;\n }\n\n /**\n * Creates a new SupabaseVectorStore instance from an existing index.\n * @param embeddings The embeddings to use.\n * @param dbConfig The configuration for the Supabase database.\n * @returns A promise that resolves with a new SupabaseVectorStore instance when the instance has been created.\n */\n static async fromExistingIndex(\n embeddings: EmbeddingsInterface,\n dbConfig: SupabaseLibArgs\n ): Promise<SupabaseVectorStore> {\n const instance = new this(embeddings, dbConfig);\n return instance;\n }\n}\n"],"mappings":"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"}