@retrivora-ai/rag-engine 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/ChromaDBProvider-QNI7UCX4.mjs +8 -0
  2. package/dist/DocumentChunker-cfaMidtA.d.mts +93 -0
  3. package/dist/DocumentChunker-cfaMidtA.d.ts +93 -0
  4. package/dist/LLMFactory-JFOY2V4X.mjs +8 -0
  5. package/dist/MilvusProvider-OO6QGZDZ.mjs +8 -0
  6. package/dist/MongoDBProvider-WWVJG3WT.mjs +8 -0
  7. package/dist/PineconeProvider-NJ675H7U.mjs +8 -0
  8. package/dist/PostgreSQLProvider-ISNMD3BE.mjs +8 -0
  9. package/dist/QdrantProvider-LJWOIGES.mjs +8 -0
  10. package/dist/RagConfig-DG_0f8ka.d.mts +145 -0
  11. package/dist/RagConfig-DG_0f8ka.d.ts +145 -0
  12. package/dist/RedisProvider-ASONNYBI.mjs +8 -0
  13. package/dist/WeaviateProvider-PSDCUGC7.mjs +8 -0
  14. package/dist/chunk-6FODXNUF.mjs +91 -0
  15. package/dist/chunk-7NXI6ZWX.mjs +89 -0
  16. package/dist/chunk-AALIF3AL.mjs +91 -0
  17. package/dist/chunk-BP4U4TT5.mjs +548 -0
  18. package/dist/chunk-HUGLYKD6.mjs +84 -0
  19. package/dist/chunk-I4E63NIC.mjs +24 -0
  20. package/dist/chunk-JI6VD5TJ.mjs +387 -0
  21. package/dist/chunk-QEYVWVT5.mjs +102 -0
  22. package/dist/chunk-S5DRHETN.mjs +110 -0
  23. package/dist/{chunk-ZPXLQR5Q.mjs → chunk-UKDXCXW7.mjs} +5 -23
  24. package/dist/chunk-V75V7BT2.mjs +117 -0
  25. package/dist/chunk-VOIWNO5O.mjs +11 -0
  26. package/dist/chunk-VPNRDXIA.mjs +74 -0
  27. package/dist/handlers/index.d.mts +9 -33
  28. package/dist/handlers/index.d.ts +9 -33
  29. package/dist/handlers/index.js +1477 -962
  30. package/dist/handlers/index.mjs +4 -2
  31. package/dist/index.d.mts +4 -3
  32. package/dist/index.d.ts +4 -3
  33. package/dist/index.js +2 -2
  34. package/dist/index.mjs +4 -2
  35. package/dist/server.d.mts +220 -53
  36. package/dist/server.d.ts +220 -53
  37. package/dist/server.js +1531 -1099
  38. package/dist/server.mjs +55 -131
  39. package/package.json +19 -2
  40. package/src/app/page.tsx +1 -1
  41. package/src/components/MessageBubble.tsx +1 -1
  42. package/src/components/SourceCard.tsx +1 -1
  43. package/src/config/RagConfig.ts +1 -1
  44. package/src/config/serverConfig.ts +2 -2
  45. package/src/core/ConfigResolver.ts +69 -0
  46. package/src/core/Pipeline.ts +101 -0
  47. package/src/core/ProviderRegistry.ts +71 -0
  48. package/src/core/VectorPlugin.ts +52 -0
  49. package/src/handlers/index.ts +21 -100
  50. package/src/hooks/useRagChat.ts +1 -1
  51. package/src/index.ts +2 -6
  52. package/src/providers/vectordb/BaseVectorProvider.ts +61 -0
  53. package/src/providers/vectordb/ChromaDBProvider.ts +94 -0
  54. package/src/providers/vectordb/MilvusProvider.ts +100 -0
  55. package/src/providers/vectordb/MongoDBProvider.ts +118 -0
  56. package/src/{vectordb/adapters/PineconeAdapter.ts → providers/vectordb/PineconeProvider.ts} +21 -56
  57. package/src/{vectordb/adapters/PgVectorAdapter.ts → providers/vectordb/PostgreSQLProvider.ts} +23 -58
  58. package/src/providers/vectordb/QdrantProvider.ts +95 -0
  59. package/src/providers/vectordb/RedisProvider.ts +84 -0
  60. package/src/providers/vectordb/WeaviateProvider.ts +124 -0
  61. package/src/server.ts +16 -8
  62. package/src/test-refactor.ts +59 -0
  63. package/src/types/index.ts +24 -0
  64. package/src/utils/templateUtils.ts +6 -6
  65. package/dist/DocumentChunker-BUrIrcPk.d.mts +0 -43
  66. package/dist/DocumentChunker-BUrIrcPk.d.ts +0 -43
  67. package/dist/RAGPipeline-BmkIv1HD.d.mts +0 -298
  68. package/dist/RAGPipeline-BmkIv1HD.d.ts +0 -298
  69. package/dist/chunk-NVOMLHXW.mjs +0 -1259
  70. package/src/rag/RAGPipeline.ts +0 -200
  71. package/src/vectordb/IVectorDB.ts +0 -75
  72. package/src/vectordb/VectorDBFactory.ts +0 -41
  73. package/src/vectordb/adapters/MongoDbAdapter.ts +0 -175
  74. package/src/vectordb/adapters/UniversalVectorDBAdapter.ts +0 -177
@@ -1,200 +0,0 @@
1
- /**
2
- * RAGPipeline — the central orchestrator for the RAG workflow.
3
- *
4
- * Responsibilities:
5
- * 1. INGEST: chunk → embed → upsert into vector DB
6
- * 2. ASK: embed query → retrieve top-K chunks → call LLM with context
7
- *
8
- * It is LLM-agnostic and vector-DB-agnostic; concrete implementations are
9
- * injected via ILLMProvider and IVectorDB.
10
- */
11
-
12
- import { IVectorDB, VectorMatch } from '../vectordb/IVectorDB';
13
- import { ILLMProvider, ChatMessage } from '../llm/ILLMProvider';
14
- import { VectorDBFactory } from '../vectordb/VectorDBFactory';
15
- import { LLMFactory } from '../llm/LLMFactory';
16
- import { DocumentChunker } from './DocumentChunker';
17
- import { RagConfig } from '../config/RagConfig';
18
-
19
- export interface IngestDocument {
20
- /** Unique document ID (used as chunk ID prefix) */
21
- docId: string;
22
- /** Full text content of the document */
23
- content: string;
24
- /** Optional metadata stored alongside each chunk */
25
- metadata?: Record<string, unknown>;
26
- }
27
-
28
- export interface ChatResponse {
29
- /** The LLM's answer */
30
- reply: string;
31
- /** Retrieved source chunks used to generate the answer */
32
- sources: VectorMatch[];
33
- }
34
-
35
- export class RAGPipeline {
36
- private readonly vectorDB: IVectorDB;
37
- private readonly llmProvider: ILLMProvider;
38
- private readonly embeddingProvider: ILLMProvider;
39
- private readonly chunker: DocumentChunker;
40
- private readonly config: RagConfig;
41
- private initialised = false;
42
-
43
- constructor(
44
- config: RagConfig,
45
- adapters?: {
46
- vectorDB?: IVectorDB;
47
- llmProvider?: ILLMProvider;
48
- embeddingProvider?: ILLMProvider;
49
- }
50
- ) {
51
- this.config = config;
52
- this.vectorDB = adapters?.vectorDB ?? VectorDBFactory.create(config.vectorDb);
53
- this.llmProvider = adapters?.llmProvider ?? LLMFactory.create(config.llm, config.embedding);
54
-
55
- // Use custom embedding provider if provided
56
- if (adapters?.embeddingProvider) {
57
- this.embeddingProvider = adapters.embeddingProvider;
58
- } else if (config.llm.provider === 'anthropic') {
59
- // If the LLM provider is Anthropic and no custom embedding is provided, spin up a dedicated embedding provider
60
- this.embeddingProvider = LLMFactory.createEmbeddingProvider(config.embedding);
61
- } else {
62
- this.embeddingProvider = this.llmProvider;
63
- }
64
-
65
- this.chunker = new DocumentChunker(
66
- config.rag?.chunkSize ?? 1000,
67
- config.rag?.chunkOverlap ?? 200
68
- );
69
- }
70
-
71
- /** Lazily initialise the vector DB connection */
72
- async init(): Promise<void> {
73
- if (!this.initialised) {
74
- await this.vectorDB.initialize();
75
- this.initialised = true;
76
- }
77
- }
78
-
79
- // ---------------------------------------------------------------------------
80
- // INGEST
81
- // ---------------------------------------------------------------------------
82
-
83
- /**
84
- * Ingest one or more documents into the vector DB.
85
- * Automatically chunks, embeds, and upserts each chunk.
86
- */
87
- async ingest(
88
- documents: IngestDocument[],
89
- namespace?: string
90
- ): Promise<{ docId: string; chunksIngested: number }[]> {
91
- await this.init();
92
-
93
- const ns = namespace ?? this.config.projectId;
94
- const results: { docId: string; chunksIngested: number }[] = [];
95
-
96
- for (const doc of documents) {
97
- const chunks = this.chunker.chunk(doc.content, {
98
- docId: doc.docId,
99
- metadata: doc.metadata,
100
- });
101
-
102
- const chunkTexts = chunks.map((c) => c.content);
103
- const vectors = await this.embeddingProvider.batchEmbed(chunkTexts);
104
-
105
- const upsertDocs = chunks.map((chunk, i) => ({
106
- id: chunk.id,
107
- vector: vectors[i],
108
- content: chunk.content,
109
- metadata: chunk.metadata,
110
- }));
111
-
112
- await this.vectorDB.batchUpsert(upsertDocs, ns);
113
- results.push({ docId: doc.docId, chunksIngested: chunks.length });
114
- console.log(`[RAGPipeline] Ingested ${chunks.length} chunks for doc: ${doc.docId}`);
115
- }
116
-
117
- return results;
118
- }
119
-
120
- // ---------------------------------------------------------------------------
121
- // ASK
122
- // ---------------------------------------------------------------------------
123
-
124
- /**
125
- * Run a full RAG query:
126
- * 1. Embed the user question
127
- * 2. Retrieve top-K relevant chunks from the vector DB
128
- * 3. Filter by score threshold
129
- * 4. Build context string
130
- * 5. Call the LLM with chat history + context
131
- */
132
- async ask(
133
- question: string,
134
- history: ChatMessage[] = [],
135
- namespace?: string
136
- ): Promise<ChatResponse> {
137
- await this.init();
138
-
139
- const ns = namespace ?? this.config.projectId;
140
- const topK = this.config.rag?.topK ?? 5;
141
- const scoreThreshold = this.config.rag?.scoreThreshold ?? 0.0;
142
-
143
- console.log(`[RAGPipeline] Searching Pinecone (Namespace: "${ns}", TopK: ${topK})...`);
144
-
145
- // 1. Embed the question
146
- const queryVector = await this.embeddingProvider.embed(question);
147
-
148
- // 2. Retrieve relevant chunks
149
- const rawMatches = await this.vectorDB.query(queryVector, topK, ns);
150
- console.log(`[RAGPipeline] Found ${rawMatches.length} raw matches in Pinecone.`);
151
-
152
- // 3. Apply score threshold filter
153
- const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
154
- console.log(`[RAGPipeline] ${sources.length} sources remaining after threshold filter (min: ${scoreThreshold}).`);
155
-
156
- // 4. Build context
157
- const context = sources.length
158
- ? sources.map((m, i) => `[Source ${i + 1}]\n${m.content}`).join('\n\n---\n\n')
159
- : 'No relevant context found.';
160
-
161
- // 5. Build message list (append current question)
162
- const messages: ChatMessage[] = [
163
- ...history,
164
- { role: 'user', content: question },
165
- ];
166
-
167
- // 6. Call the LLM
168
- const reply = await this.llmProvider.chat(messages, context);
169
-
170
- return { reply, sources };
171
- }
172
-
173
- // ---------------------------------------------------------------------------
174
- // HEALTH
175
- // ---------------------------------------------------------------------------
176
-
177
- async health(): Promise<{ vectorDB: boolean; llm: boolean }> {
178
- const [vectorDB, llm] = await Promise.all([
179
- this.vectorDB.ping().catch(() => false),
180
- this.llmProvider.ping().catch(() => false),
181
- ]);
182
- return { vectorDB, llm };
183
- }
184
-
185
- // ---------------------------------------------------------------------------
186
- // DATA MANAGEMENT
187
- // ---------------------------------------------------------------------------
188
-
189
- async deleteDocument(docId: string, namespace?: string): Promise<void> {
190
- await this.init();
191
- const ns = namespace ?? this.config.projectId;
192
- await this.vectorDB.delete(docId, ns);
193
- }
194
-
195
- async deleteProject(namespace?: string): Promise<void> {
196
- await this.init();
197
- const ns = namespace ?? this.config.projectId;
198
- await this.vectorDB.deleteNamespace(ns);
199
- }
200
- }
@@ -1,75 +0,0 @@
1
- /**
2
- * Generic Vector Database interface.
3
- * Any adapter (Pinecone, pgVector, REST, Chroma, Qdrant, …) must implement this.
4
- */
5
-
6
- export interface VectorMatch {
7
- /** Unique identifier of the stored chunk */
8
- id: string;
9
- /** Cosine or dot-product similarity score (0–1) */
10
- score: number;
11
- /** The original text content of the chunk */
12
- content: string;
13
- /** Arbitrary metadata attached at upsert time */
14
- metadata?: Record<string, unknown>;
15
- }
16
-
17
- export interface UpsertDocument {
18
- id: string;
19
- vector: number[];
20
- content: string;
21
- metadata?: Record<string, unknown>;
22
- }
23
-
24
- export interface IVectorDB {
25
- /**
26
- * Initialise the connection (create tables, verify index, etc.)
27
- * Called once during RAGPipeline construction.
28
- */
29
- initialize(): Promise<void>;
30
-
31
- /**
32
- * Insert or update a single vector + content pair.
33
- * @param namespace – optional project/tenant namespace
34
- */
35
- upsert(doc: UpsertDocument, namespace?: string): Promise<void>;
36
-
37
- /**
38
- * Batch upsert for efficient ingestion of many documents.
39
- */
40
- batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void>;
41
-
42
- /**
43
- * Find the top-K most similar vectors to the query vector.
44
- * @param vector – embedding of the user query
45
- * @param topK – number of results to return
46
- * @param namespace – optional project/tenant namespace
47
- * @param filter – optional metadata filter (provider-specific shape)
48
- */
49
- query(
50
- vector: number[],
51
- topK: number,
52
- namespace?: string,
53
- filter?: Record<string, unknown>
54
- ): Promise<VectorMatch[]>;
55
-
56
- /**
57
- * Delete a stored vector by ID.
58
- */
59
- delete(id: string, namespace?: string): Promise<void>;
60
-
61
- /**
62
- * Delete all vectors in a namespace (full data reset for a project).
63
- */
64
- deleteNamespace(namespace: string): Promise<void>;
65
-
66
- /**
67
- * Check if the underlying DB is reachable.
68
- */
69
- ping(): Promise<boolean>;
70
-
71
- /**
72
- * Gracefully close the connection.
73
- */
74
- disconnect(): Promise<void>;
75
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * VectorDBFactory — instantiates the correct IVectorDB adapter
3
- * based on the VectorDBConfig.provider value.
4
- *
5
- * Usage:
6
- * const db = VectorDBFactory.create(config.vectorDb);
7
- * await db.initialize();
8
- */
9
-
10
- import { IVectorDB } from './IVectorDB';
11
- import { VectorDBConfig } from '../config/RagConfig';
12
- import { PineconeAdapter } from './adapters/PineconeAdapter';
13
- import { PgVectorAdapter } from './adapters/PgVectorAdapter';
14
- import { UniversalVectorDBAdapter } from './adapters/UniversalVectorDBAdapter';
15
- import { MongoDbAdapter } from './adapters/MongoDbAdapter';
16
-
17
- export class VectorDBFactory {
18
- static create(config: VectorDBConfig): IVectorDB {
19
- switch (config.provider) {
20
- case 'pinecone':
21
- return new PineconeAdapter(config);
22
- case 'pgvector':
23
- return new PgVectorAdapter(config);
24
- case 'mongodb':
25
- return new MongoDbAdapter(config.options);
26
- case 'rest':
27
- case 'universal_rest':
28
- case 'custom':
29
- return new UniversalVectorDBAdapter(config);
30
- default:
31
- // Fallback to Universal Adapter if baseUrl is present in options
32
- if ((config.options as Record<string, unknown>)?.baseUrl) {
33
- return new UniversalVectorDBAdapter(config);
34
- }
35
- throw new Error(
36
- `[VectorDBFactory] Unknown provider "${(config as VectorDBConfig).provider}". ` +
37
- `Supported: pinecone | pgvector | mongodb | rest`
38
- );
39
- }
40
- }
41
- }
@@ -1,175 +0,0 @@
1
- import { MongoClient, Collection, Db } from 'mongodb';
2
- import { IVectorDB, UpsertDocument, VectorMatch } from '../IVectorDB';
3
-
4
- interface MongoDoc {
5
- _id: string;
6
- [key: string]: unknown;
7
- }
8
-
9
- /**
10
- * MongoDB Adapter for Atlas Vector Search.
11
- *
12
- * Requirements:
13
- * 1. A MongoDB Atlas cluster (v6.0.11+, v7.0.2+).
14
- * 2. A Vector Search Index created on the collection.
15
- *
16
- * Config Options:
17
- * - uri: string (Required)
18
- * - database: string (Required)
19
- * - collection: string (Required)
20
- * - indexName: string (Default: 'vector_index')
21
- * - embeddingKey: string (Default: 'embedding')
22
- * - contentKey: string (Default: 'content')
23
- * - metadataKey: string (Default: 'metadata')
24
- */
25
- export class MongoDbAdapter implements IVectorDB {
26
- private client: MongoClient;
27
- private db?: Db;
28
- private collection?: Collection<MongoDoc>;
29
- private uri: string;
30
- private dbName: string;
31
- private collectionName: string;
32
- private indexName: string;
33
- private embeddingKey: string;
34
- private contentKey: string;
35
- private metadataKey: string;
36
-
37
- constructor(options: Record<string, unknown>) {
38
- this.uri = options.uri as string;
39
- this.dbName = options.database as string;
40
- this.collectionName = options.collection as string;
41
- this.indexName = (options.indexName as string) || 'vector_index';
42
- this.embeddingKey = (options.embeddingKey as string) || 'embedding';
43
- this.contentKey = (options.contentKey as string) || 'content';
44
- this.metadataKey = (options.metadataKey as string) || 'metadata';
45
-
46
- if (!this.uri || !this.dbName || !this.collectionName) {
47
- throw new Error('MongoDbAdapter requires uri, database, and collection options.');
48
- }
49
-
50
- this.client = new MongoClient(this.uri);
51
- }
52
-
53
- async initialize(): Promise<void> {
54
- await this.client.connect();
55
- this.db = this.client.db(this.dbName);
56
- this.collection = this.db.collection(this.collectionName);
57
- }
58
-
59
- async upsert(doc: UpsertDocument, namespace?: string): Promise<void> {
60
- if (!this.collection) await this.initialize();
61
-
62
- const document = {
63
- _id: doc.id as string,
64
- [this.embeddingKey]: doc.vector,
65
- [this.contentKey]: doc.content,
66
- [this.metadataKey]: doc.metadata || {},
67
- ...(namespace ? { namespace } : {}),
68
- };
69
-
70
- await this.collection!.updateOne(
71
- { _id: doc.id as string },
72
- { $set: document },
73
- { upsert: true }
74
- );
75
- }
76
-
77
- async batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void> {
78
- if (!this.collection) await this.initialize();
79
-
80
- const operations = docs.map((doc) => ({
81
- updateOne: {
82
- filter: { _id: doc.id as string },
83
- update: {
84
- $set: {
85
- _id: doc.id as string,
86
- [this.embeddingKey]: doc.vector,
87
- [this.contentKey]: doc.content,
88
- [this.metadataKey]: doc.metadata || {},
89
- ...(namespace ? { namespace } : {}),
90
- },
91
- },
92
- upsert: true,
93
- },
94
- }));
95
-
96
- await this.collection!.bulkWrite(operations);
97
- }
98
-
99
- async query(
100
- vector: number[],
101
- topK: number,
102
- namespace?: string,
103
- filter?: Record<string, unknown>
104
- ): Promise<VectorMatch[]> {
105
- if (!this.collection) await this.initialize();
106
-
107
- const pipeline: Record<string, unknown>[] = [
108
- {
109
- $vectorSearch: {
110
- index: this.indexName,
111
- path: this.embeddingKey,
112
- queryVector: vector,
113
- numCandidates: Math.max(topK * 10, 100),
114
- limit: topK,
115
- ...(filter || namespace
116
- ? {
117
- filter: {
118
- ...(filter || {}),
119
- ...(namespace ? { namespace } : {}),
120
- },
121
- }
122
- : {}),
123
- },
124
- },
125
- {
126
- $project: {
127
- score: { $meta: 'vectorSearchScore' },
128
- content: `$${this.contentKey}`,
129
- metadata: `$${this.metadataKey}`,
130
- },
131
- },
132
- ];
133
-
134
- const results = await this.collection!.aggregate(pipeline).toArray();
135
-
136
- return (results as unknown as MongoDoc[]).map((res) => ({
137
- id: res._id.toString(),
138
- score: res.score as number,
139
- content: res.content as string,
140
- metadata: res.metadata as Record<string, unknown>,
141
- }));
142
- }
143
-
144
- async delete(id: string, namespace?: string): Promise<void> {
145
- if (!this.collection) await this.initialize();
146
- const query = { _id: id, ...(namespace ? { namespace } : {}) };
147
- await this.collection!.deleteOne(query as unknown as MongoDoc);
148
- }
149
-
150
- async deleteNamespace(namespace: string): Promise<void> {
151
- if (!this.collection) await this.initialize();
152
- await this.collection!.deleteMany({ namespace });
153
- }
154
-
155
- async ping(): Promise<boolean> {
156
- try {
157
- if (!this.collection) await this.initialize();
158
- await this.db!.command({ ping: 1 });
159
- return true;
160
- } catch (err) {
161
- console.error('[MongoDbAdapter] Ping failed:', err);
162
- return false;
163
- }
164
- }
165
-
166
- async disconnect(): Promise<void> {
167
- try {
168
- await this.client.close();
169
- this.db = undefined;
170
- this.collection = undefined;
171
- } catch (err) {
172
- console.error('[MongoDbAdapter] Failed to disconnect:', err);
173
- }
174
- }
175
- }
@@ -1,177 +0,0 @@
1
- import axios, { AxiosInstance } from 'axios';
2
- import { IVectorDB, VectorMatch, UpsertDocument } from '../IVectorDB';
3
- import { VectorDBConfig } from '../../config/RagConfig';
4
- import { resolvePath, buildPayload } from '../../utils/templateUtils';
5
- import { VECTOR_PROFILES } from '../../config/UniversalProfiles';
6
-
7
- interface UniversalVectorDBOptions {
8
- baseUrl?: string;
9
- headers?: Record<string, string>;
10
- queryPath?: string;
11
- upsertPath?: string;
12
- deletePath?: string;
13
- deleteNamespacePath?: string;
14
- pingPath?: string;
15
- queryPayloadTemplate?: string;
16
- upsertPayloadTemplate?: string;
17
- batchUpsertPayloadTemplate?: string;
18
- responseExtractPath?: string;
19
- idPath?: string;
20
- scorePath?: string;
21
- contentPath?: string;
22
- metadataPath?: string;
23
- timeout?: number;
24
- }
25
-
26
- export class UniversalVectorDBAdapter implements IVectorDB {
27
- private readonly http: AxiosInstance;
28
- private readonly indexName: string;
29
- private readonly opts: UniversalVectorDBOptions;
30
-
31
- constructor(config: VectorDBConfig) {
32
- this.indexName = config.indexName;
33
-
34
- // Merge profile defaults if specified
35
- const options = (config.options as Record<string, unknown>) ?? {};
36
- let profile: Record<string, unknown> = {};
37
-
38
- if (typeof options.profile === 'string') {
39
- profile = (VECTOR_PROFILES as Record<string, Record<string, unknown>>)[options.profile] || {};
40
- } else if (typeof options.profile === 'object' && options.profile !== null) {
41
- profile = options.profile as Record<string, unknown>;
42
- }
43
-
44
- this.opts = { ...profile, ...(config.options ?? {}) } as UniversalVectorDBOptions;
45
-
46
- if (!this.opts.baseUrl) {
47
- throw new Error('[UniversalVectorDBAdapter] options.baseUrl is required');
48
- }
49
-
50
- this.http = axios.create({
51
- baseURL: this.opts.baseUrl,
52
- headers: {
53
- 'Content-Type': 'application/json',
54
- ...(this.opts.headers || {}),
55
- },
56
- timeout: this.opts.timeout ?? 30_000,
57
- });
58
- }
59
-
60
- async initialize(): Promise<void> {
61
- if (this.opts.pingPath) {
62
- const ok = await this.ping();
63
- if (!ok) {
64
- throw new Error(`[UniversalVectorDBAdapter] Could not reach health endpoint: ${this.opts.pingPath}`);
65
- }
66
- }
67
- }
68
-
69
- async upsert(doc: UpsertDocument, namespace?: string): Promise<void> {
70
- const path = this.opts.upsertPath ?? '/upsert';
71
-
72
- if (this.opts.upsertPayloadTemplate) {
73
- const payload = buildPayload(this.opts.upsertPayloadTemplate, {
74
- id: doc.id,
75
- vector: doc.vector,
76
- content: doc.content,
77
- metadata: doc.metadata ?? {},
78
- namespace: namespace ?? '',
79
- index: this.indexName,
80
- });
81
- await this.http.post(path, payload);
82
- } else {
83
- // Fallback to standard generic shape
84
- await this.http.post(path, {
85
- index: this.indexName,
86
- namespace,
87
- ...doc,
88
- });
89
- }
90
- }
91
-
92
- async batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void> {
93
- const path = this.opts.upsertPath ?? '/upsert';
94
-
95
- // For batch upsert with templating, if the user provided batchUpsertPayloadTemplate
96
- if (this.opts.batchUpsertPayloadTemplate) {
97
- const payload = buildPayload(this.opts.batchUpsertPayloadTemplate, {
98
- vectors: docs,
99
- namespace: namespace ?? '',
100
- index: this.indexName,
101
- });
102
- await this.http.post(path, payload);
103
- } else {
104
- // Fallback to standard loop or generic shape
105
- await Promise.all(docs.map((doc) => this.upsert(doc, namespace)));
106
- }
107
- }
108
-
109
- async query(
110
- vector: number[],
111
- topK: number,
112
- namespace?: string,
113
- filter?: Record<string, unknown>
114
- ): Promise<VectorMatch[]> {
115
- const path = this.opts.queryPath ?? '/query';
116
-
117
- let payload: unknown;
118
- if (this.opts.queryPayloadTemplate) {
119
- payload = buildPayload(this.opts.queryPayloadTemplate, {
120
- vector,
121
- topK,
122
- namespace: namespace ?? '',
123
- index: this.indexName,
124
- filter: filter ?? {},
125
- });
126
- } else {
127
- payload = { index: this.indexName, namespace, vector, topK, filter };
128
- }
129
-
130
- const { data } = await this.http.post(path, payload);
131
-
132
- const extractPath = this.opts.responseExtractPath ?? 'matches';
133
- const matchesRaw = resolvePath(data, extractPath);
134
-
135
- if (!Array.isArray(matchesRaw)) {
136
- throw new Error(`[UniversalVectorDBAdapter] Expected an array at responseExtractPath '${extractPath}', but got ${typeof matchesRaw}`);
137
- }
138
-
139
- const idPath = this.opts.idPath ?? 'id';
140
- const scorePath = this.opts.scorePath ?? 'score';
141
- const contentPath = this.opts.contentPath ?? 'content';
142
- const metadataPath = this.opts.metadataPath ?? 'metadata';
143
-
144
- return matchesRaw.map((match) => ({
145
- id: String(resolvePath(match, idPath) || ''),
146
- score: Number(resolvePath(match, scorePath) || 0),
147
- content: String(resolvePath(match, contentPath) || ''),
148
- metadata: (resolvePath(match, metadataPath) as Record<string, unknown> | undefined) || {},
149
- }));
150
- }
151
-
152
- async delete(id: string, namespace?: string): Promise<void> {
153
- const path = this.opts.deletePath ?? '/delete';
154
- await this.http.post(path, { index: this.indexName, namespace, id });
155
- }
156
-
157
- async deleteNamespace(namespace: string): Promise<void> {
158
- const path = this.opts.deleteNamespacePath ?? '/delete-namespace';
159
- await this.http.post(path, { index: this.indexName, namespace });
160
- }
161
-
162
- async ping(): Promise<boolean> {
163
- try {
164
- if (this.opts.pingPath) {
165
- await this.http.get(this.opts.pingPath);
166
- }
167
- return true;
168
- } catch (err) {
169
- console.error('[UniversalVectorDBAdapter] Ping failed:', err);
170
- return false;
171
- }
172
- }
173
-
174
- async disconnect(): Promise<void> {
175
- // Axios based adapter is stateless per request.
176
- }
177
- }