{"version":3,"file":"azion_edgesql.cjs","names":["BaseRetriever","SystemMessage","HumanMessage","Document"],"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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"}