{"version":3,"file":"vectorstores.cjs","names":["VectorStore","QdrantClient","Document"],"sources":["../src/vectorstores.ts"],"sourcesContent":["import { QdrantClient } from \"@qdrant/js-client-rest\";\nimport type { Schemas as QdrantSchemas } from \"@qdrant/js-client-rest\";\nimport { v4 as uuid } from \"@langchain/core/utils/uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport {\n  type MaxMarginalRelevanceSearchOptions,\n  VectorStore,\n} from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\n\nconst CONTENT_KEY = \"content\";\nconst METADATA_KEY = \"metadata\";\n\n/**\n * Interface for the arguments that can be passed to the\n * `QdrantVectorStore` constructor. It includes options for specifying a\n * `QdrantClient` instance, the URL and API key for a Qdrant database, and\n * the name and configuration for a collection.\n */\nexport interface QdrantLibArgs {\n  client?: QdrantClient;\n  url?: string;\n  apiKey?: string;\n  collectionName?: string;\n  collectionConfig?: QdrantSchemas[\"CreateCollection\"];\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  customPayload?: Record<string, any>[];\n  contentPayloadKey?: string;\n  metadataPayloadKey?: string;\n}\n\nexport type QdrantAddDocumentOptions = {\n  ids?: string[];\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  customPayload?: Record<string, any>[];\n};\n\n/**\n * Type that defines the parameters for the delete operation in the\n * QdrantStore class. It includes ids, filter and shard key.\n */\nexport type QdrantDeleteParams =\n  | { ids: string[]; shardKey?: string; filter?: never }\n  | { filter: object; shardKey?: string; ids?: never };\n\nexport type QdrantFilter = QdrantSchemas[\"Filter\"];\n\nexport type QdrantCondition = QdrantSchemas[\"FieldCondition\"];\n\n/**\n * Type for the response returned by a search operation in the Qdrant\n * database. It includes the score and payload (metadata and content) for\n * each point (document) in the search results.\n */\ntype QdrantSearchResponse = QdrantSchemas[\"ScoredPoint\"] & {\n  payload: {\n    metadata: object;\n    content: string;\n  };\n};\n\n/**\n * Class that extends the `VectorStore` base class to interact with a\n * Qdrant database. It includes methods for adding documents and vectors\n * to the Qdrant database, searching for similar vectors, and ensuring the\n * existence of a collection in the database.\n */\nexport class QdrantVectorStore extends VectorStore {\n  declare FilterType: QdrantFilter;\n\n  get lc_secrets(): { [key: string]: string } {\n    return {\n      apiKey: \"QDRANT_API_KEY\",\n      url: \"QDRANT_URL\",\n    };\n  }\n\n  client: QdrantClient;\n\n  collectionName: string;\n\n  collectionConfig?: QdrantSchemas[\"CreateCollection\"];\n\n  contentPayloadKey: string;\n\n  metadataPayloadKey: string;\n\n  _vectorstoreType(): string {\n    return \"qdrant\";\n  }\n\n  constructor(embeddings: EmbeddingsInterface, args: QdrantLibArgs) {\n    super(embeddings, args);\n\n    const url = args.url ?? getEnvironmentVariable(\"QDRANT_URL\");\n    const apiKey = args.apiKey ?? getEnvironmentVariable(\"QDRANT_API_KEY\");\n\n    if (!args.client && !url) {\n      throw new Error(\"Qdrant client or url address must be set.\");\n    }\n\n    this.client =\n      args.client ||\n      new QdrantClient({\n        url,\n        apiKey,\n      });\n\n    this.collectionName = args.collectionName ?? \"documents\";\n\n    this.collectionConfig = args.collectionConfig;\n\n    this.contentPayloadKey = args.contentPayloadKey ?? CONTENT_KEY;\n\n    this.metadataPayloadKey = args.metadataPayloadKey ?? METADATA_KEY;\n  }\n\n  /**\n   * Method to add documents to the Qdrant database. It generates vectors\n   * from the documents using the `Embeddings` instance and then adds the\n   * vectors to the database.\n   * @param documents Array of `Document` instances to be added to the Qdrant database.\n   * @param documentOptions Optional `QdrantAddDocumentOptions` which has a list of JSON objects for extra querying\n   * @returns Promise that resolves when the documents have been added to the database.\n   */\n  async addDocuments(\n    documents: Document[],\n    documentOptions?: QdrantAddDocumentOptions\n  ): Promise<void> {\n    const texts = documents.map(({ pageContent }) => pageContent);\n    await this.addVectors(\n      await this.embeddings.embedDocuments(texts),\n      documents,\n      documentOptions\n    );\n  }\n\n  /**\n   * Method to add vectors to the Qdrant database. Each vector is associated\n   * with a document, which is stored as the payload for a point in the\n   * database.\n   * @param vectors Array of vectors to be added to the Qdrant database.\n   * @param documents Array of `Document` instances associated with the vectors.\n   * @param documentOptions Optional `QdrantAddDocumentOptions` which has a list of JSON objects for extra querying\n   * @returns Promise that resolves when the vectors have been added to the database.\n   */\n  async addVectors(\n    vectors: number[][],\n    documents: Document[],\n    documentOptions?: QdrantAddDocumentOptions\n  ): Promise<void> {\n    if (vectors.length === 0) {\n      return;\n    }\n\n    await this.ensureCollection();\n\n    const points = vectors.map((embedding, idx) => ({\n      id: documents[idx].id ?? documentOptions?.ids?.[idx] ?? uuid(),\n      vector: embedding,\n      payload: {\n        [this.contentPayloadKey]: documents[idx].pageContent,\n        [this.metadataPayloadKey]: documents[idx].metadata,\n        customPayload: documentOptions?.customPayload?.[idx],\n      },\n    }));\n\n    try {\n      await this.client.upsert(this.collectionName, {\n        wait: true,\n        points,\n      });\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    } catch (e: any) {\n      const error = new Error(\n        `${e?.status ?? \"Undefined error code\"} ${e?.message}: ${\n          e?.data?.status?.error\n        }`\n      );\n      throw error;\n    }\n  }\n\n  /**\n   * Method that deletes points from the Qdrant database.\n   * @param params Parameters for the delete operation.\n   * @returns Promise that resolves when the delete operation is complete.\n   */\n  async delete(params: QdrantDeleteParams): Promise<void> {\n    const { ids, filter, shardKey } = params;\n\n    if (ids) {\n      const batchSize = 1000;\n      for (let i = 0; i < ids.length; i += batchSize) {\n        const batchIds = ids.slice(i, i + batchSize);\n        await this.client.delete(this.collectionName, {\n          wait: true,\n          ordering: \"weak\",\n          points: batchIds,\n          shard_key: shardKey,\n        });\n      }\n    } else if (filter) {\n      await this.client.delete(this.collectionName, {\n        wait: true,\n        ordering: \"weak\",\n        filter,\n        shard_key: shardKey,\n      });\n    } else {\n      throw new Error(\"Either ids or filter must be provided.\");\n    }\n  }\n\n  /**\n   * Method to search for vectors in the Qdrant database that are similar to\n   * a given query vector. The search results include the score and payload\n   * (metadata and content) for each similar vector.\n   * @param query Query vector to search for similar vectors in the Qdrant database.\n   * @param k Optional number of similar vectors to return. If not specified, all similar vectors are returned.\n   * @param filter Optional filter to apply to the search results.\n   * @returns Promise that resolves with an array of tuples, where each tuple includes a `Document` instance and a score for a similar vector.\n   */\n  async similaritySearchVectorWithScore(\n    query: number[],\n    k?: number,\n    filter?: this[\"FilterType\"]\n  ): Promise<[Document, number][]> {\n    if (!query) {\n      return [];\n    }\n\n    await this.ensureCollection();\n\n    const results = (\n      await this.client.query(this.collectionName, {\n        query,\n        limit: k,\n        filter,\n        with_payload: [this.metadataPayloadKey, this.contentPayloadKey],\n        with_vector: false,\n      })\n    ).points;\n\n    const result: [Document, number][] = (\n      results as QdrantSearchResponse[]\n    ).map((res) => [\n      new Document({\n        id: res.id as string,\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        metadata: res.payload[this.metadataPayloadKey] as Record<string, any>,\n        pageContent: res.payload[this.contentPayloadKey] as string,\n      }),\n      res.score,\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 - Number of documents to fetch before passing to the MMR algorithm. Defaults to 20.\n   * @param {number} options.lambda - 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 {this[\"FilterType\"]} options.filter - Optional filter to apply to the search results.\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    if (!query) {\n      return [];\n    }\n\n    const queryEmbedding = await this.embeddings.embedQuery(query);\n\n    await this.ensureCollection();\n\n    const results = (\n      await this.client.query(this.collectionName, {\n        query: {\n          nearest: queryEmbedding,\n          mmr: {\n            diversity: options.lambda ?? null,\n            candidates_limit: options?.fetchK ?? 20,\n          },\n        },\n        limit: options.k,\n        filter: options?.filter,\n        with_payload: [this.metadataPayloadKey, this.contentPayloadKey],\n        with_vector: true,\n      })\n    ).points;\n\n    const result = (results as QdrantSearchResponse[]).map(\n      (res) =>\n        new Document({\n          id: res.id as string,\n          // eslint-disable-next-line @typescript-eslint/no-explicit-any\n          metadata: res.payload[this.metadataPayloadKey] as Record<string, any>,\n          pageContent: res.payload[this.contentPayloadKey] as string,\n        })\n    );\n\n    return result;\n  }\n\n  /**\n   * Method to ensure the existence of a collection in the Qdrant database.\n   * If the collection does not exist, it is created.\n   * @returns Promise that resolves when the existence of the collection has been ensured.\n   */\n  async ensureCollection() {\n    const response = await this.client.getCollections();\n\n    const collectionNames = response.collections.map(\n      (collection) => collection.name\n    );\n\n    if (!collectionNames.includes(this.collectionName)) {\n      const collectionConfig = this.collectionConfig ?? {\n        vectors: {\n          size: (await this.embeddings.embedQuery(\"test\")).length,\n          distance: \"Cosine\",\n        },\n      };\n      await this.client.createCollection(this.collectionName, collectionConfig);\n    }\n  }\n\n  /**\n   * Static method to create a `QdrantVectorStore` instance from texts. Each\n   * text is associated with metadata and converted to a `Document`\n   * instance, which is then added to the Qdrant database.\n   * @param texts Array of texts to be converted to `Document` instances and added to the Qdrant database.\n   * @param metadatas Array or single object of metadata to be associated with the texts.\n   * @param embeddings `Embeddings` instance used to generate vectors from the texts.\n   * @param dbConfig `QdrantLibArgs` instance specifying the configuration for the Qdrant database.\n   * @returns Promise that resolves with a new `QdrantVectorStore` instance.\n   */\n  static async fromTexts(\n    texts: string[],\n    metadatas: object[] | object,\n    embeddings: EmbeddingsInterface,\n    dbConfig: QdrantLibArgs\n  ): Promise<QdrantVectorStore> {\n    const docs = [];\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 QdrantVectorStore.fromDocuments(docs, embeddings, dbConfig);\n  }\n\n  /**\n   * Static method to create a `QdrantVectorStore` instance from `Document`\n   * instances. The documents are added to the Qdrant database.\n   * @param docs Array of `Document` instances to be added to the Qdrant database.\n   * @param embeddings `Embeddings` instance used to generate vectors from the documents.\n   * @param dbConfig `QdrantLibArgs` instance specifying the configuration for the Qdrant database.\n   * @returns Promise that resolves with a new `QdrantVectorStore` instance.\n   */\n  static async fromDocuments(\n    docs: Document[],\n    embeddings: EmbeddingsInterface,\n    dbConfig: QdrantLibArgs\n  ): Promise<QdrantVectorStore> {\n    const instance = new this(embeddings, dbConfig);\n    if (dbConfig.customPayload) {\n      const documentOptions = {\n        customPayload: dbConfig?.customPayload,\n      };\n      await instance.addDocuments(docs, documentOptions);\n    } else {\n      await instance.addDocuments(docs);\n    }\n    return instance;\n  }\n\n  /**\n   * Static method to create a `QdrantVectorStore` instance from an existing\n   * collection in the Qdrant database.\n   * @param embeddings `Embeddings` instance used to generate vectors from the documents in the collection.\n   * @param dbConfig `QdrantLibArgs` instance specifying the configuration for the Qdrant database.\n   * @returns Promise that resolves with a new `QdrantVectorStore` instance.\n   */\n  static async fromExistingCollection(\n    embeddings: EmbeddingsInterface,\n    dbConfig: QdrantLibArgs\n  ): Promise<QdrantVectorStore> {\n    const instance = new this(embeddings, dbConfig);\n    await instance.ensureCollection();\n    return instance;\n  }\n}\n"],"mappings":"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"}