@langchain/community
Version:
Third-party integrations for LangChain.js
1 lines • 22.8 kB
Source Map (JSON)
{"version":3,"file":"azion_edgesql.cjs","names":["BaseRetriever","embeddings: EmbeddingsInterface","args: AzionRetrieverArgs","filters: AzionFilter[]","embeddedQuery: number[]","queryEntities: string","metadata: string","query: string","SystemMessage","HumanMessage","error:\n | {\n message: string;\n operation: string;\n }\n | undefined","result: [Document][]","results: SearchEmbeddingsResponse[]","uniqueResults: SearchEmbeddingsResponse[]","results: QueryResult[] | undefined","queryResult: QueryResult","searches: SearchEmbeddingsResponse[]","resp: SearchEmbeddingsResponse","Document","item: string | undefined"],"sources":["../../src/retrievers/azion_edgesql.ts"],"sourcesContent":["import { QueryResult, useQuery } from \"azion/sql\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { Document } from \"@langchain/core/documents\";\nimport { BaseRetriever, BaseRetrieverInput } from \"@langchain/core/retrievers\";\nimport { SystemMessage, HumanMessage } from \"@langchain/core/messages\";\nimport { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\n\n/**\n * Represents a filter condition for querying the Azion database\n * @property operator - The comparison operator to use (e.g. =, !=, >, <, etc)\n * @property column - The database column to filter on\n * @property value - The value to compare against\n */\nexport type AzionFilter = { operator: Operator; column: Column; value: string };\n\n/**\n * Represents a database column name\n */\nexport type Column = string;\n\n/**\n * Valid SQL operators that can be used in filter conditions\n */\nexport type Operator =\n | \"=\"\n | \"!=\"\n | \">\"\n | \"<>\"\n | \"<\" // Basic comparison operators\n | \">=\"\n | \"<=\" // Range operators\n | \"LIKE\"\n | \"NOT LIKE\" // Pattern matching\n | \"IN\"\n | \"NOT IN\" // Set membership\n | \"IS NULL\"\n | \"IS NOT NULL\"; // NULL checks\n\n/**\n * Interface for the response returned when searching embeddings.\n */\ninterface SearchEmbeddingsResponse {\n id: number;\n content: string;\n metadata: {\n searchtype: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Interface for the arguments required to initialize an Azion library.\n */\nexport interface AzionRetrieverArgs extends BaseRetrieverInput {\n /**\n * Search type to perform. Cosine similarity and hybrid (vector + FTS) are currently supported.\n */\n searchType?: \"hybrid\" | \"similarity\";\n\n /**\n * The number of documents retrieved with cosine similarity (vector) search. Minimum is 1.\n */\n similarityK?: number;\n\n /**\n * The number of documents retrieved with full text search. Minimum is 1.\n */\n ftsK?: number;\n\n /**\n * The name of the database to search for documents.\n */\n dbName?: string;\n\n /**\n * The prompt to the chatmodel to extract entities to perform Full text search on the database\n */\n promptEntityExtractor?: string;\n\n /**\n * The chatmodel to extract entities to perform Full text search on the database\n */\n entityExtractor?: BaseChatModel;\n\n /**\n * Max items to maintain per searchtype. Default is 3.\n */\n maxItemsSearch?: number;\n\n /**\n * The columns from the tables that metadata must contain\n */\n metadataItems?: string[];\n\n /**\n * Name of the table to perform vector similarity seach. Default is 'documents'\n */\n vectorTable?: string;\n\n /**\n * Name of the table to perform full text search. Default is 'document_fts'\n */\n ftsTable?: string;\n\n /**\n * Filters to apply to the search. Default is an empty array.\n */\n filters?: AzionFilter[];\n\n /** Whether the metadata is contained in a single column or multiple columns */\n expandedMetadata?: boolean;\n}\n\n/**\n * class for performing hybrid search operations on Azion's Edge SQL database.\n * It extends the 'BaseRetriever' class and implements methods for\n * similarity search and full-text search (FTS).\n */\n/**\n * Example usage:\n * ```ts\n * // Initialize embeddings and chat model\n * const embeddings = new OpenAIEmbeddings();\n * const chatModel = new ChatOpenAI({ model: \"gpt-4o-mini\" });\n *\n * // Create retriever with hybrid search\n * const retriever = new AzionRetriever(embeddings, chatModel, {\n * searchType: 'hybrid',\n * similarityK: 3,\n * ftsK: 2,\n * dbName: 'my_docs',\n * metadataItems: ['category', 'author'],\n * vectorTable: 'documents',\n * ftsTable: 'documents_fts',\n * filters: [\n * { operator: '=', column: 'status', value: 'published' }\n * ]\n * });\n *\n * // Retrieve relevant documents\n * const docs = await retriever.invoke(\n * \"What are coral reefs in Australia?\"\n * );\n *\n * // Create retriever with similarity search only\n * const simRetriever = new AzionRetriever(embeddings, chatModel, {\n * searchType: 'similarity',\n * similarityK: 5,\n * dbName: 'my_docs',\n * vectorTable: 'documents'\n * });\n *\n * // Customize entity extraction prompt\n * const customRetriever = new AzionRetriever(embeddings, chatModel, {\n * searchType: 'hybrid',\n * similarityK: 3,\n * ftsK: 2,\n * dbName: 'my_docs',\n * promptEntityExtractor: \"Extract key entities from: {{query}}\"\n * });\n * ```\n */\n\nexport class AzionRetriever extends BaseRetriever {\n static lc_name() {\n return \"azionRetriever\";\n }\n\n /** Namespace for the retriever in LangChain */\n lc_namespace = [\"langchain\", \"retrievers\", \"azion\"];\n\n /** Type of search to perform - either hybrid (combining vector + FTS) or similarity only */\n searchType?: \"hybrid\" | \"similarity\";\n\n /** Number of results to return from similarity search. Minimum is 1. */\n similarityK: number;\n\n /** Number of results to return from full text search. Minimum is 1. */\n ftsK: number;\n\n /** Interface for generating embeddings from text */\n embeddings: EmbeddingsInterface;\n\n /** Name of the database to search */\n dbName: string;\n\n /** Optional ChatModel used to extract entities from queries */\n entityExtractor?: BaseChatModel;\n\n /** Prompt template for entity extraction */\n promptEntityExtractor: string;\n\n /** Optional metadata columns to include in results */\n metadataItems?: string[];\n\n /** Name of table containing vector embeddings for similarity search */\n vectorTable: string;\n\n /** Name of table containing documents for full text search */\n ftsTable: string;\n\n /** Array of filters to apply to search results */\n filters: AzionFilter[];\n\n /** Whether the metadata is contained in a single column or multiple columns */\n expandedMetadata: boolean;\n\n constructor(embeddings: EmbeddingsInterface, args: AzionRetrieverArgs) {\n super(args);\n\n this.ftsTable = args.ftsTable || \"vectors_fts\";\n this.vectorTable = args.vectorTable || \"vectors\";\n this.similarityK = Math.max(1, args.similarityK || 1);\n this.ftsK = Math.max(1, args.ftsK || 1);\n this.dbName = args.dbName || \"vectorstore\";\n\n this.embeddings = embeddings;\n this.searchType = args.searchType || \"similarity\";\n\n this.entityExtractor = args.entityExtractor || undefined;\n this.metadataItems = args.metadataItems || undefined;\n this.promptEntityExtractor =\n args.promptEntityExtractor ||\n \"Provide them as a space-separated string in lowercase, translated to English.\";\n this.filters = args.filters || [];\n this.expandedMetadata = args.expandedMetadata || false;\n }\n\n /**\n * Generates a string of filters for the SQL query.\n * @param {AzionFilter[]} filters - The filters to apply to the search.\n * @returns {string} A string of filters for the SQL query.\n */\n protected generateFilters(filters: AzionFilter[]): string {\n if (!filters || filters?.length === 0) {\n return \"\";\n }\n\n return `${filters\n .map(({ operator, column, value }) => {\n const columnRef = this.expandedMetadata\n ? this.sanitizeItem(column)\n : `metadata->>'$.${this.sanitizeItem(column)}'`;\n if ([\"IN\", \"NOT IN\"].includes(operator.toUpperCase())) {\n return `${columnRef} ${operator} (${this.sanitizeItem(value)})`;\n }\n return `${columnRef} ${operator} '${this.sanitizeItem(value)}'`;\n })\n .join(\" AND \")} AND `;\n }\n\n /**\n * Generates SQL queries for full-text search and similarity search.\n * @param {number[]} embeddedQuery - The embedded query vector.\n * @param {string} queryEntities - The entities extracted from the query for full-text search.\n * @param {string} metadata - Additional metadata columns to be included in the results.\n * @returns An object containing the FTS query and similarity query strings.\n */\n protected generateSqlQueries(\n embeddedQuery: number[],\n queryEntities: string,\n metadata: string\n ): { ftsQuery: string; similarityQuery: string } {\n const filters = this.generateFilters(this.filters);\n\n let rowsNumber = this.similarityK;\n if (this.searchType === \"hybrid\") {\n rowsNumber += this.ftsK;\n }\n\n const ftsQuery = `\n SELECT id, content, ${metadata.replace(\"hybrid\", \"fts\")}\n FROM ${this.ftsTable} \n WHERE ${filters} ${this.ftsTable} MATCH '${queryEntities}'\n ORDER BY rank \n LIMIT ${rowsNumber}\n `;\n\n const similarityQuery = `\n SELECT id, content, ${metadata.replace(\"hybrid\", \"similarity\")}\n FROM ${this.vectorTable} \n WHERE ${filters} rowid IN vector_top_k('${\n this.vectorTable\n }_idx', vector('[${embeddedQuery}]'), ${rowsNumber})\n `;\n\n return { ftsQuery, similarityQuery };\n }\n\n /**\n * Generates the SQL statements for the similarity search and full-text search.\n * @param query The user query.\n * @returns An array of SQL statements.\n */\n protected async generateStatements(query: string): Promise<string[]> {\n const embeddedQuery = await this.embeddings.embedQuery(query);\n\n const metadata = this.generateMetadata();\n\n let queryEntities = \"\";\n if (this.searchType === \"hybrid\") {\n queryEntities = await this.extractEntities(query);\n }\n\n const { ftsQuery, similarityQuery } = this.generateSqlQueries(\n embeddedQuery,\n queryEntities,\n metadata\n );\n\n if (this.searchType === \"similarity\") {\n return [similarityQuery];\n }\n\n return [similarityQuery, ftsQuery];\n }\n\n /**\n * Generates the metadata string for the SQL query.\n * @returns {string} The metadata string.\n */\n protected generateMetadata(): string {\n if (!this.metadataItems) {\n return `json_object('searchtype', '${this.searchType}') as metadata`;\n }\n\n if (this.expandedMetadata) {\n return `json_object('searchtype','${this.searchType}',${this.metadataItems\n .map(\n (item) => `'${this.sanitizeItem(item)}', ${this.sanitizeItem(item)}`\n )\n .join(\", \")}) as metadata`;\n }\n\n return `json_patch(json_object(${this.metadataItems\n ?.map(\n (item) =>\n `'${this.sanitizeItem(item)}', metadata->>'$.${this.sanitizeItem(\n item\n )}'`\n )\n .join(\", \")}), '{\"searchtype\":\"${this.searchType}\"}') as metadata`;\n }\n\n /**\n * Performs a similarity search on the vector store and returns the top 'similarityK' similar documents.\n * @param query The query string.\n * @returns A promise that resolves with the similarity search results when the search is complete.\n */\n protected async similaritySearchWithScore(\n query: string\n ): Promise<[Document][]> {\n const statements = await this.generateStatements(query);\n\n const { data: response, error: errorQuery } = await useQuery(\n this.dbName,\n statements\n );\n\n if (!response) {\n console.error(\"RESPONSE ERROR: \", errorQuery);\n throw this.searchError(errorQuery);\n }\n const searches = this.mapRows(response.results);\n const result = this.mapSearches(searches);\n return result;\n }\n\n /**\n * Extracts entities from a user query using the entityExtractor model.\n * @param query The user query\n * @returns A promise that resolves with the extracted entities when the extraction is complete.\n */\n protected async extractEntities(query: string): Promise<string> {\n if (!this.entityExtractor) {\n return this.convert2FTSQuery(query);\n }\n\n const entityExtractionPrompt = new SystemMessage(\n this.promptEntityExtractor\n );\n\n const entityQuery = await this.entityExtractor.invoke([\n entityExtractionPrompt,\n new HumanMessage(query),\n ]);\n return this.convert2FTSQuery(entityQuery.content.toString());\n }\n\n /**\n * Converts a query to a FTS query.\n * @param query The user query\n * @returns The converted FTS query\n */\n protected convert2FTSQuery(query: string): string {\n return query\n .replace(/[^a-záàâãéèêíïóôõöúçñA-ZÁÀÂÃÉÈÊÍÏÓÔÕÖÚÇÑ0-9\\s]/g, \"\") // Remove special chars keeping accents\n .replace(/\\s+/g, \" \") // Remove multiple spaces\n .trim() // Remove leading/trailing spaces\n .split(\" \")\n .join(\" OR \");\n }\n\n /**\n * Performs a hybrid search on the vector store, using cosine similarity and FTS search, and\n * returns the top 'similarityK' + 'ftsK' similar documents.\n * @param query The user query\n * @returns A promise that resolves with the hybrid search results when the search is complete.\n */\n protected async hybridSearchAzion(query: string): Promise<[Document][]> {\n const statements = await this.generateStatements(query);\n\n const { data: response, error: errorQuery } = await useQuery(\n this.dbName,\n statements\n );\n\n if (!response) {\n console.error(\"RESPONSE ERROR: \", errorQuery);\n throw this.searchError(errorQuery);\n }\n\n const results = this.mapRows(response.results);\n\n const finalResults = this.removeDuplicates(results);\n\n return this.mapSearches(finalResults);\n }\n\n /**\n * Generates an error document based on the provided error information\n * @param error The error object containing details about the issue\n * @returns A promise that resolves to an array containing a single Document representing the error\n */\n protected searchError(\n error:\n | {\n message: string;\n operation: string;\n }\n | undefined\n ): Error {\n throw new Error(error?.message);\n }\n\n /**\n * Performs the selected search and returns the documents retrieved.\n * @param query The user query\n * @returns A promise that resolves with the completion of the search results.\n */\n async _getRelevantDocuments(query: string): Promise<Document[]> {\n let result: [Document][];\n\n if (this.searchType === \"similarity\") {\n result = await this.similaritySearchWithScore(query);\n } else {\n result = await this.hybridSearchAzion(query);\n }\n\n return result.map(([doc]) => doc);\n }\n\n /**\n * Removes duplicate results from the search results, prioritizing a mix of similarity and FTS results.\n * @param {SearchEmbeddingsResponse[]} results - The array of search results to process.\n * @returns {SearchEmbeddingsResponse[]} An array of unique search results, with a maximum of 3 similarity and 3 FTS results.\n */\n private removeDuplicates(\n results: SearchEmbeddingsResponse[]\n ): SearchEmbeddingsResponse[] {\n const uniqueResults: SearchEmbeddingsResponse[] = [];\n const seenIds = new Set<number>();\n\n let similarityCount = 0;\n let ftsCount = 0;\n const maxItems = this.ftsK + this.similarityK;\n\n for (const result of results) {\n if (!seenIds.has(result.id)) {\n if (\n result.metadata.searchtype === \"similarity\" &&\n similarityCount < this.similarityK\n ) {\n seenIds.add(result.id);\n uniqueResults.push(result);\n similarityCount += 1;\n } else if (\n result.metadata.searchtype === \"fts\" &&\n ftsCount < this.ftsK\n ) {\n seenIds.add(result.id);\n uniqueResults.push(result);\n ftsCount += 1;\n }\n }\n if (similarityCount + ftsCount === maxItems) break;\n }\n return uniqueResults;\n }\n\n /**\n * Converts query results to SearchEmbeddingsResponse objects.\n * @param {QueryResult[]} results - The raw query results from the database.\n * @returns {SearchEmbeddingsResponse[]} An array of SearchEmbeddingsResponse objects.\n */\n private mapRows(\n results: QueryResult[] | undefined\n ): SearchEmbeddingsResponse[] {\n if (!results) {\n return [];\n }\n\n return results.flatMap(\n (queryResult: QueryResult): SearchEmbeddingsResponse[] => {\n if (!queryResult.rows || !queryResult.columns) {\n return [];\n }\n\n return queryResult.rows.map(\n (row): SearchEmbeddingsResponse => ({\n id: Number(row[0]),\n content: String(row[1]),\n metadata: JSON.parse(String(row[2])),\n })\n );\n }\n );\n }\n\n /**\n * Maps search results to Document objects.\n * @param {SearchEmbeddingsResponse[]} searches An array of SearchEmbeddingsResponse objects.\n * @returns An array of tuples, each containing a single Document object.\n */\n protected mapSearches(searches: SearchEmbeddingsResponse[]): [Document][] {\n return searches.map((resp: SearchEmbeddingsResponse) => [\n new Document({\n metadata: resp.metadata,\n pageContent: resp.content,\n id: resp.id.toString(),\n }),\n ]);\n }\n\n /**\n * Sanitizes an item by removing non-alphanumeric characters.\n * @param {string} item The item to sanitize.\n * @returns {string} The sanitized item.\n */\n private sanitizeItem(item: string | undefined): string {\n if (item) {\n return item.replace(/[^a-zA-Z0-9\\s]/g, \"\");\n }\n return \"\";\n }\n}\n"],"mappings":"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"}