UNPKG

@prexo/ai-chat-sdk

Version:

AI Chat Component with Persistent History

154 lines (153 loc) 5.15 kB
import { DEFAULT_SIMILARITY_THRESHOLD, DEFAULT_TOP_K } from "../../lib/constants.js"; import { nanoid } from "nanoid"; import { BASE_API_ENDPOINT } from "../../lib/utils.js"; function chunkText(text, chunkSize = 500, overlap = 50) { const chunks = []; for (let i = 0; i < text.length; i += chunkSize - overlap) { chunks.push(text.slice(i, i + chunkSize)); } return chunks; } class VectorDB { index; constructor(index) { this.index = index; } async reset(options) { await this.index.reset({ namespace: options?.namespace }); } async delete({ ids, namespace }) { await this.index.delete(ids, { namespace }); } /** * A method that allows you to query the vector database with plain text. * It takes care of the text-to-embedding conversion by itself. * Additionally, it lets consumers pass various options to tweak the output. */ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters async retrieve({ question, similarityThreshold = DEFAULT_SIMILARITY_THRESHOLD, topK = DEFAULT_TOP_K, namespace, contextFilter, queryMode }) { const index = this.index; const result = await index.query( { ...typeof question === "string" ? { data: question } : { vector: question }, topK, includeData: true, includeMetadata: true, ...typeof contextFilter === "string" && { filter: contextFilter }, queryMode }, { namespace } ); const allValuesUndefined = result.every((embedding) => embedding.data === void 0); if (allValuesUndefined) { console.error("There is no answer for this question in the provided context."); return [ { data: "There is no answer for this question in the provided context.", id: "error", metadata: {} } ]; } const facts = result.filter((x) => x.score >= similarityThreshold).map((embedding) => ({ data: embedding.data ?? "", id: embedding.id.toString(), metadata: embedding.metadata })); return facts; } /** * A method that allows you to add various data types into a vector database. * It supports plain text, embeddings, PDF, HTML, Text file and CSV. Additionally, it handles text-splitting for CSV, PDF and Text file. */ async save(input) { const { namespace } = input.options ?? {}; if (input.type === "text") { try { const returnId = input.id ?? nanoid(); await this.index.upsert( { data: input.data, id: returnId, metadata: input.options?.metadata }, { namespace } ); return { success: true, ids: [returnId.toString()] }; } catch (error) { return { success: false, error: JSON.stringify(error, Object.getOwnPropertyNames(error)) }; } } else if (input.type === "embedding") { try { const returnId = input.id ?? nanoid(); await this.index.upsert( { vector: input.data, data: input.text, id: returnId, metadata: input.options?.metadata }, { namespace } ); return { success: true, ids: [returnId.toString()] }; } catch (error) { return { success: false, error: JSON.stringify(error, Object.getOwnPropertyNames(error)) }; } } else { try { let fileSource = void 0; if ("fileSource" in input && typeof input.fileSource === "string") { fileSource = input.fileSource; } else if ("source" in input && typeof input.source === "string") { fileSource = input.source; } if (!fileSource) { throw new Error("No valid file source provided for extractText"); } const response = await fetch(`${BASE_API_ENDPOINT}/extractor`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${process.env.PREXO_API_KEY}` }, body: JSON.stringify({ url: fileSource }) }); if (!response.ok) { throw new Error(`Failed to extract text: ${response.status} ${response.statusText}`); } const res = await response.json(); const transformArgs = "config" in input ? input.config : {}; const chunkSize = transformArgs && transformArgs.chunkSize || 500; const overlap = transformArgs && transformArgs.overlap || 100; const chunks = chunkText(res.output.txt, chunkSize, overlap); const ids = []; for (const chunk of chunks) { const id = nanoid(); await this.index.upsert( { data: chunk, id, metadata: input.options?.metadata }, { namespace } ); ids.push(id); } return { success: true, ids }; } catch (error) { console.error(error); return { success: false, error: JSON.stringify(error, Object.getOwnPropertyNames(error)) }; } } } } export { VectorDB };