"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TurbopufferVectorStore = void 0; const uuid_1 = require("uuid"); const documents_1 = require("@langchain/core/documents"); const async_caller_1 = require("@langchain/core/utils/async_caller"); const chunk_array_1 = require("@langchain/core/utils/chunk_array"); const env_1 = require("@langchain/core/utils/env"); const vectorstores_1 = require("@langchain/core/vectorstores"); class TurbopufferVectorStore extends vectorstores_1.VectorStore { get lc_secrets() { return { apiKey: "TURBOPUFFER_API_KEY", }; } get lc_aliases() { return { apiKey: "TURBOPUFFER_API_KEY", }; } // Handle minification for tracing static lc_name() { return "TurbopufferVectorStore"; } _vectorstoreType() { return "turbopuffer"; } constructor(embeddings, args) { super(embeddings, args); Object.defineProperty(this, "distanceMetric", { enumerable: true, configurable: true, writable: true, value: "cosine_distance" }); Object.defineProperty(this, "apiKey", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "namespace", { enumerable: true, configurable: true, writable: true, value: "default" }); Object.defineProperty(this, "apiUrl", { enumerable: true, configurable: true, writable: true, value: "https://api.turbopuffer.com/v1" }); Object.defineProperty(this, "caller", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "batchSize", { enumerable: true, configurable: true, writable: true, value: 3000 }); const { apiKey: argsApiKey, namespace, distanceMetric, apiUrl, batchSize, ...asyncCallerArgs } = args; const apiKey = argsApiKey ?? (0, env_1.getEnvironmentVariable)("TURBOPUFFER_API_KEY"); if (!apiKey) { throw new Error(`Turbopuffer API key not found.\nPlease pass it in as "apiKey" or set it as an environment variable called "TURBOPUFFER_API_KEY"`); } this.apiKey = apiKey; this.namespace = namespace ?? this.namespace; this.distanceMetric = distanceMetric ?? this.distanceMetric; this.apiUrl = apiUrl ?? this.apiUrl; this.batchSize = batchSize ?? this.batchSize; this.caller = new async_caller_1.AsyncCaller({ maxConcurrency: 6, maxRetries: 0, ...asyncCallerArgs, }); } defaultHeaders() { return { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", }; } async callWithRetry(fetchUrl, stringifiedBody, method = "POST") { const json = await this.caller.call(async () => { const headers = { Authorization: `Bearer ${this.apiKey}`, }; if (stringifiedBody !== undefined) { headers["Content-Type"] = "application/json"; } const response = await fetch(fetchUrl, { method, headers, body: stringifiedBody, }); if (response.status !== 200) { const error = new Error(`Failed to call turbopuffer. Response status ${response.status}\nFull response: ${await response.text()}`); // eslint-disable-next-line @typescript-eslint/no-explicit-any error.response = response; throw error; } return response.json(); }); return json; } async addVectors(vectors, documents, options) { if (options?.ids && options.ids.length !== vectors.length) { throw new Error("Number of ids provided does not match number of vectors"); } if (documents.length !== vectors.length) { throw new Error("Number of documents provided does not match number of vectors"); } if (documents.length === 0) { throw new Error("No documents provided"); } const batchedVectors = (0, chunk_array_1.chunkArray)(vectors, this.batchSize); const batchedDocuments = (0, chunk_array_1.chunkArray)(documents, this.batchSize); const batchedIds = options?.ids ? (0, chunk_array_1.chunkArray)(options.ids, this.batchSize) : batchedDocuments.map((docs) => docs.map((_) => (0, uuid_1.v4)())); const batchRequests = batchedVectors.map(async (batchVectors, index) => { const batchDocs = batchedDocuments[index]; const batchIds = batchedIds[index]; if (batchIds.length !== batchVectors.length) { throw new Error("Number of ids provided does not match number of vectors"); } const attributes = { __lc_page_content: batchDocs.map((doc) => doc.pageContent), }; const usedMetadataFields = new Set(batchDocs.map((doc) => Object.keys(doc.metadata)).flat()); for (const key of usedMetadataFields) { attributes[key] = batchDocs.map((doc) => { if (doc.metadata[key] !== undefined) { if (typeof doc.metadata[key] === "string") { return doc.metadata[key]; } else { console.warn([ `[WARNING]: Dropping non-string metadata key "${key}" with value "${JSON.stringify(doc.metadata[key])}".`, `turbopuffer currently supports only string metadata values.`, ].join("\n")); return null; } } else { return null; } }); } const data = { ids: batchIds, vectors: batchVectors, attributes, }; return this.callWithRetry(`${this.apiUrl}/vectors/${this.namespace}`, JSON.stringify(data)); }); // Execute all batch requests in parallel await Promise.all(batchRequests); return batchedIds.flat(); } async delete(params) { if (params.deleteIndex) { await this.callWithRetry(`${this.apiUrl}/vectors/${this.namespace}`, undefined, "DELETE"); } else { throw new Error(`You must provide a "deleteIndex" flag.`); } } async addDocuments(documents, options) { const vectors = await this.embeddings.embedDocuments(documents.map((doc) => doc.pageContent)); return this.addVectors(vectors, documents, options); } async queryVectors(query, k, includeVector, // See https://Turbopuffer.com/docs/reference/query for more info filter) { const data = { vector: query, top_k: k, distance_metric: this.distanceMetric, filters: filter, include_attributes: true, include_vectors: includeVector, }; return this.callWithRetry(`${this.apiUrl}/vectors/${this.namespace}/query`, JSON.stringify(data)); } async similaritySearchVectorWithScore(query, k, filter) { const search = await this.queryVectors(query, k, false, filter); const result = search.map((res) => { const { __lc_page_content, ...metadata } = res.attributes; return [ new documents_1.Document({ pageContent: __lc_page_content, metadata, }), res.dist, ]; }); return result; } static async fromDocuments(docs, embeddings, dbConfig) { const instance = new this(embeddings, dbConfig); await instance.addDocuments(docs); return instance; } } exports.TurbopufferVectorStore = TurbopufferVectorStore;