@prexo/ai-chat-sdk
Version:
AI Chat SDK for building AI chat applications with Context and History
1 lines • 16.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/context/ext-vector.ts","../src/lib/constants.ts","../src/lib/firecrawl.ts","../src/context/vector-db.ts","../src/context/client.ts"],"names":["DEFAULT_SIMILARITY_THRESHOLD","DEFAULT_TOP_K","firecrawlClient","getFirecrawlApiKey","getFirecrawl","key","Firecrawl","scrapeContent","url","client","chunkText","text","chunkSize","overlap","chunks","i","VectorDB","index","__publicField","options","ids","namespace","question","similarityThreshold","topK","contextFilter","queryMode","result","embedding","x","input","returnId","nanoid","error","fileSource","res","transformArgs","markdown","summary","chunk","id","ExtVector","config","Index","payload","getContextClient","params","vectorUrl","vectorToken"],"mappings":"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","file":"context.cjs","sourcesContent":["import { Index } from \"@upstash/vector\";\nimport { VectorDB } from \"./vector-db\";\nimport type {\n AddContextPayload,\n VectorPayload,\n SaveOperationResult,\n} from \"../types\";\n\nexport type ExtVectorConfig = {\n url: string;\n token: string;\n};\n\nexport class ExtVector {\n private vectorDB: VectorDB;\n private namespace: string;\n\n constructor(config: ExtVectorConfig, namespace: string) {\n const index = new Index({\n url: config.url,\n token: config.token,\n });\n this.vectorDB = new VectorDB(index);\n this.namespace = namespace;\n }\n\n async addContext(input: AddContextPayload): Promise<SaveOperationResult> {\n // Always use the provided namespace\n if (!input.options) input.options = {};\n input.options.namespace = this.namespace;\n return this.vectorDB.save(input);\n }\n\n async removeContext(ids: string[]): Promise<void> {\n await this.vectorDB.delete({ ids, namespace: this.namespace });\n }\n\n async getContext<TMetadata>(\n payload: Omit<VectorPayload, \"namespace\">,\n ): Promise<{ data: string; id: string; metadata: TMetadata }[]> {\n return this.vectorDB.retrieve<TMetadata>({\n ...payload,\n namespace: this.namespace,\n });\n }\n\n async resetContext(): Promise<void> {\n await this.vectorDB.reset({ namespace: this.namespace });\n }\n}\n","// Constants for the AI Chat SDK\nexport const DEFAULT_CHAT_SESSION_ID = (id?: string) =>\n `prexo-chat-session/${id ? id : \"default\"}`;\n\nexport const DEFAULT_HISTORY_LENGTH = 50;\nexport const DEFAULT_HISTORY_TTL = 86_400;\n\nexport const DEFAULT_MSG_ID = `msg-${Date.now().toString()}`;\nexport const DEFAULT_SIMILARITY_THRESHOLD = 0.5;\nexport const DEFAULT_TOP_K = 5;\n","import Firecrawl from '@mendable/firecrawl-js';\n\n// Lazily initialize Firecrawl only when an API key is available.\n// This avoids throwing during client-side bundles where process.env is not present.\nlet firecrawlClient: Firecrawl | undefined;\n\nfunction getFirecrawlApiKey(): string | undefined {\n try {\n // In Node/bundler environments, process may exist\n if (typeof process !== 'undefined' && process.env && process.env.FIRECRAWL_API_KEY) {\n return process.env.FIRECRAWL_API_KEY;\n }\n } catch {\n // ignore\n }\n return undefined;\n}\n\nfunction getFirecrawl(): Firecrawl | undefined {\n const key = getFirecrawlApiKey();\n if (!key) return undefined;\n if (!firecrawlClient) {\n firecrawlClient = new Firecrawl({ apiKey: key });\n }\n return firecrawlClient;\n}\n\nexport async function scrapeContent(url: string) {\n const client = getFirecrawl();\n if (!client) {\n throw new Error(\"Firecrawl is not configured. Set FIRECRAWL_API_KEY on the server to enable scraping.\");\n }\n const data = await client.scrape(url, { formats: ['markdown', 'summary'] });\n return data;\n}","import type {\n AddContextPayload,\n ResetOptions,\n SaveOperationResult,\n VectorPayload,\n} from \"../types\";\nimport { DEFAULT_SIMILARITY_THRESHOLD, DEFAULT_TOP_K } from \"../lib/constants\";\nimport type { Index } from \"@upstash/vector\";\nimport { nanoid } from \"nanoid\";\nimport { scrapeContent } from \"../lib/firecrawl\";\n\n// Helper function to chunk text\nfunction chunkText(text: string, chunkSize = 500, overlap = 50): string[] {\n const chunks: string[] = [];\n for (let i = 0; i < text.length; i += chunkSize - overlap) {\n chunks.push(text.slice(i, i + chunkSize));\n }\n return chunks;\n}\n\nexport class VectorDB {\n private index: Index;\n constructor(index: Index) {\n this.index = index;\n }\n\n async reset(options?: ResetOptions) {\n await this.index.reset({ namespace: options?.namespace });\n }\n\n async delete({ ids, namespace }: { ids: string[]; namespace?: string }) {\n await this.index.delete(ids, { namespace });\n }\n\n /**\n * A method that allows you to query the vector database with plain text.\n * It takes care of the text-to-embedding conversion by itself.\n * Additionally, it lets consumers pass various options to tweak the output.\n */\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters\n async retrieve<TMetadata>({\n question,\n similarityThreshold = DEFAULT_SIMILARITY_THRESHOLD,\n topK = DEFAULT_TOP_K,\n namespace,\n contextFilter,\n queryMode,\n }: VectorPayload): Promise<\n { data: string; id: string; metadata: TMetadata }[]\n > {\n const index = this.index;\n const result = await index.query<Record<string, string>>(\n {\n ...(typeof question === \"string\"\n ? { data: question }\n : { vector: question }),\n topK,\n includeData: true,\n includeMetadata: true,\n ...(typeof contextFilter === \"string\" && { filter: contextFilter }),\n queryMode,\n },\n { namespace },\n );\n console.log(\"Vector Retrive: \", result)\n const allValuesUndefined = result.every(\n (embedding) => embedding.data === undefined,\n );\n\n if (allValuesUndefined) {\n console.error(\n \"There is no answer for this question in the provided context.\",\n );\n\n return [\n {\n data: \"There is no answer for this question in the provided context.\",\n id: \"error\",\n metadata: {} as TMetadata,\n },\n ];\n }\n\n const facts = result\n .filter((x) => x.score >= similarityThreshold)\n .map((embedding) => ({\n data: embedding.data ?? \"\",\n id: embedding.id.toString(),\n metadata: embedding.metadata as TMetadata,\n }));\n\n return facts;\n }\n\n /**\n * A method that allows you to add various data types into a vector database.\n * It supports plain text, embeddings, PDF, HTML, Text file and CSV. Additionally, it handles text-splitting for CSV, PDF and Text file.\n */\n async save(input: AddContextPayload): Promise<SaveOperationResult> {\n const { namespace } = input.options ?? {};\n if (input.type === \"text\") {\n try {\n const returnId = input.id ?? nanoid();\n await this.index.upsert(\n {\n data: input.data,\n id: returnId,\n metadata: input.options?.metadata,\n },\n { namespace },\n );\n\n return { success: true, ids: [returnId.toString()] };\n } catch (error) {\n return {\n success: false,\n error: JSON.stringify(error, Object.getOwnPropertyNames(error)),\n };\n }\n } else if (input.type === \"embedding\") {\n try {\n const returnId = input.id ?? nanoid();\n await this.index.upsert(\n {\n vector: input.data,\n data: input.text,\n id: returnId,\n metadata: input.options?.metadata,\n },\n { namespace },\n );\n\n return { success: true, ids: [returnId.toString()] };\n } catch (error) {\n return {\n success: false,\n error: JSON.stringify(error, Object.getOwnPropertyNames(error)),\n };\n }\n } else {\n try {\n // Determine the file source (string path or URL)\n let fileSource: string | undefined = undefined;\n if (\"fileSource\" in input && typeof input.fileSource === \"string\") {\n fileSource = input.fileSource;\n } else if (\"source\" in input && typeof input.source === \"string\") {\n fileSource = input.source;\n }\n if (!fileSource) {\n throw new Error(\"No valid file source provided for extractText\");\n }\n\n // extractText should return a string (the extracted text)\n const res = await scrapeContent(fileSource);\n if (!res?.markdown && !res?.summary) {\n throw new Error(\"Failed to extract data from the provided source\");\n }\n\n // chunk the extracted text\n const transformArgs = \"config\" in input ? input.config : {};\n // If transformArgs has chunkSize/overlap, use them, else default\n const chunkSize =\n (transformArgs && (transformArgs as any).chunkSize) || 500;\n const overlap =\n (transformArgs && (transformArgs as any).overlap) || 100;\n\n // Prefer markdown for chunking; fallbacks for possible shapes\n const markdown =\n (res as any).markdown ??\n (res as any)?.output?.markdown ??\n (res as any)?.output?.txt ??\n \"\";\n const summary =\n (res as any).summary ?? (res as any)?.output?.summary;\n\n const chunks = chunkText(markdown, chunkSize, overlap);\n\n // upsert each chunk\n const ids: string[] = [];\n for (const chunk of chunks) {\n const id = nanoid();\n await this.index.upsert(\n {\n data: chunk,\n id,\n metadata: {\n ...(input.options?.metadata ?? {}),\n ...(summary ? { summary } : {}),\n },\n },\n { namespace },\n );\n ids.push(id);\n }\n\n return { success: true, ids };\n } catch (error) {\n console.error(error);\n return {\n success: false,\n error: JSON.stringify(error, Object.getOwnPropertyNames(error)),\n };\n }\n }\n }\n}\n","\"use client\";\nimport type { BaseVectorContext, GetContextClientParams } from \"../types\";\nimport { ExtVector } from \"./ext-vector\";\n\nexport const getContextClient = (\n params?: GetContextClientParams,\n): BaseVectorContext | undefined => {\n const vectorUrl = params?.vector?.url;\n const vectorToken = params?.vector?.token;\n // Prefer container_id as namespace, fallback to provided vector.namespace\n const namespace = params?.vector?.namespace;\n\n if (vectorUrl && vectorToken && namespace) {\n return new ExtVector({ url: vectorUrl, token: vectorToken }, namespace);\n }\n};\n"]}