@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,44 +1,14 @@
1
- /**
2
- * createChatHandler — factory that returns a Next.js App Router POST handler
3
- * for the RAG chat endpoint.
4
- *
5
- * Use this when you want to mount the RAG chat API inside your OWN Next.js app
6
- * (i.e. you installed @rag/ai-accelerator as a package and want the API route
7
- * to live at your project's /api/chat).
8
- *
9
- * Usage — in your project's `src/app/api/chat/route.ts`:
10
- *
11
- * import { createChatHandler } from '@rag/ai-accelerator/handlers';
12
- * import myConfig from '@/config/ragConfig';
13
- * export const POST = createChatHandler(myConfig);
14
- *
15
- * Or with env-based config:
16
- *
17
- * import { createChatHandler } from '@rag/ai-accelerator/handlers';
18
- * import { getRagConfig } from '@rag/ai-accelerator';
19
- * export const POST = createChatHandler(getRagConfig());
20
- */
21
-
22
1
  import { NextRequest, NextResponse } from 'next/server';
23
- import { RAGPipeline } from '../rag/RAGPipeline';
2
+ import { VectorPlugin } from '../core/VectorPlugin';
24
3
  import { RagConfig } from '../config/RagConfig';
25
- import { ChatMessage, ILLMProvider } from '../llm/ILLMProvider';
26
- import { IVectorDB } from '../vectordb/IVectorDB';
4
+ import { ChatMessage } from '../llm/ILLMProvider';
27
5
  import { DocumentParser } from '../utils/DocumentParser';
28
6
 
29
- export interface CustomAdapters {
30
- vectorDB?: IVectorDB;
31
- llmProvider?: ILLMProvider;
32
- embeddingProvider?: ILLMProvider;
33
- }
34
-
35
- export function createChatHandler(config: RagConfig, adapters?: CustomAdapters) {
36
- let pipeline: RAGPipeline | null = null;
37
-
38
- function getPipeline(): RAGPipeline {
39
- if (!pipeline) pipeline = new RAGPipeline(config, adapters);
40
- return pipeline;
41
- }
7
+ /**
8
+ * createChatHandler — factory that returns a Next.js App Router POST handler
9
+ */
10
+ export function createChatHandler(config?: Partial<RagConfig>) {
11
+ const plugin = new VectorPlugin(config);
42
12
 
43
13
  return async function POST(req: NextRequest) {
44
14
  try {
@@ -53,8 +23,7 @@ export function createChatHandler(config: RagConfig, adapters?: CustomAdapters)
53
23
  return NextResponse.json({ error: 'message is required' }, { status: 400 });
54
24
  }
55
25
 
56
- const rag = getPipeline();
57
- const result = await rag.ask(message, history, namespace);
26
+ const result = await plugin.chat(message, history, namespace);
58
27
  return NextResponse.json(result);
59
28
  } catch (err) {
60
29
  const message = err instanceof Error ? err.message : 'Internal server error';
@@ -65,19 +34,9 @@ export function createChatHandler(config: RagConfig, adapters?: CustomAdapters)
65
34
 
66
35
  /**
67
36
  * createIngestHandler — factory for the document ingestion endpoint.
68
- *
69
- * Usage — in your project's `src/app/api/ingest/route.ts`:
70
- *
71
- * import { createIngestHandler } from '@rag/ai-accelerator/handlers';
72
- * export const POST = createIngestHandler(myConfig, { vectorDB: new MyCustomDB() });
73
37
  */
74
- export function createIngestHandler(config: RagConfig, adapters?: CustomAdapters) {
75
- let pipeline: RAGPipeline | null = null;
76
-
77
- function getPipeline(): RAGPipeline {
78
- if (!pipeline) pipeline = new RAGPipeline(config, adapters);
79
- return pipeline;
80
- }
38
+ export function createIngestHandler(config?: Partial<RagConfig>) {
39
+ const plugin = new VectorPlugin(config);
81
40
 
82
41
  return async function POST(req: NextRequest) {
83
42
  try {
@@ -88,16 +47,11 @@ export function createIngestHandler(config: RagConfig, adapters?: CustomAdapters
88
47
  };
89
48
 
90
49
  if (!Array.isArray(documents) || documents.length === 0) {
91
- return NextResponse.json(
92
- { error: 'documents array is required and must not be empty' },
93
- { status: 400 }
94
- );
50
+ return NextResponse.json({ error: 'documents array is required' }, { status: 400 });
95
51
  }
96
52
 
97
- const rag = getPipeline();
98
- const results = await rag.ingest(documents, namespace);
99
- const totalChunks = results.reduce((sum, r) => sum + r.chunksIngested, 0);
100
- return NextResponse.json({ results, totalChunks });
53
+ const results = await plugin.ingest(documents, namespace);
54
+ return NextResponse.json({ results });
101
55
  } catch (err) {
102
56
  const message = err instanceof Error ? err.message : 'Internal server error';
103
57
  return NextResponse.json({ error: message }, { status: 500 });
@@ -107,56 +61,31 @@ export function createIngestHandler(config: RagConfig, adapters?: CustomAdapters
107
61
 
108
62
  /**
109
63
  * createHealthHandler — factory for the health-check endpoint.
110
- *
111
- * Usage — in your project's `src/app/api/health/route.ts`:
112
- *
113
- * import { createHealthHandler } from '@rag/ai-accelerator/handlers';
114
- * export const GET = createHealthHandler(myConfig, { vectorDB: new MyCustomDB() });
115
64
  */
116
- export function createHealthHandler(config: RagConfig, adapters?: CustomAdapters) {
117
- let pipeline: RAGPipeline | null = null;
118
-
119
- function getPipeline(): RAGPipeline {
120
- if (!pipeline) pipeline = new RAGPipeline(config, adapters);
121
- return pipeline;
122
- }
65
+ export function createHealthHandler(config?: Partial<RagConfig>) {
66
+ const plugin = new VectorPlugin(config);
123
67
 
124
68
  return async function GET() {
125
69
  try {
126
- const rag = getPipeline();
127
- const health = await rag.health();
70
+ const health = await plugin.health();
128
71
  const status = health.vectorDB && health.llm ? 'ok' : 'degraded';
129
72
  return NextResponse.json({
130
73
  status,
131
74
  ...health,
132
- project: config.projectId,
133
75
  timestamp: new Date().toISOString(),
134
76
  });
135
77
  } catch (err) {
136
78
  const message = err instanceof Error ? err.message : 'Unknown error';
137
- return NextResponse.json(
138
- { status: 'error', error: message, timestamp: new Date().toISOString() },
139
- { status: 500 }
140
- );
79
+ return NextResponse.json({ status: 'error', error: message }, { status: 500 });
141
80
  }
142
81
  };
143
82
  }
144
83
 
145
84
  /**
146
85
  * createUploadHandler — factory for the file upload ingestion endpoint.
147
- *
148
- * Usage — in your project's `src/app/api/upload/route.ts`:
149
- *
150
- * import { createUploadHandler } from '@rag/ai-accelerator/handlers';
151
- * export const POST = createUploadHandler(myConfig);
152
86
  */
153
- export function createUploadHandler(config: RagConfig, adapters?: CustomAdapters) {
154
- let pipeline: RAGPipeline | null = null;
155
-
156
- function getPipeline(): RAGPipeline {
157
- if (!pipeline) pipeline = new RAGPipeline(config, adapters);
158
- return pipeline;
159
- }
87
+ export function createUploadHandler(config?: Partial<RagConfig>) {
88
+ const plugin = new VectorPlugin(config);
160
89
 
161
90
  return async function POST(req: NextRequest) {
162
91
  try {
@@ -184,16 +113,8 @@ export function createUploadHandler(config: RagConfig, adapters?: CustomAdapters
184
113
  })
185
114
  );
186
115
 
187
- const rag = getPipeline();
188
- const results = await rag.ingest(documents, namespace);
189
- const totalChunks = results.reduce((sum, r) => sum + r.chunksIngested, 0);
190
-
191
- return NextResponse.json({
192
- message: 'Upload successful',
193
- filesProcessed: files.length,
194
- totalChunks,
195
- results,
196
- });
116
+ const results = await plugin.ingest(documents, namespace);
117
+ return NextResponse.json({ message: 'Upload successful', results });
197
118
  } catch (err) {
198
119
  const message = err instanceof Error ? err.message : 'Upload failed';
199
120
  return NextResponse.json({ error: message }, { status: 500 });
@@ -15,7 +15,7 @@
15
15
 
16
16
  import { useState, useCallback, useRef, useEffect } from 'react';
17
17
  import axios from 'axios';
18
- import { VectorMatch } from '@/vectordb/IVectorDB';
18
+ import { VectorMatch } from '@/types';
19
19
  import { useStoredMessages } from './useStoredMessages';
20
20
 
21
21
  // ─── Types ────────────────────────────────────────────────────────────────────
package/src/index.ts CHANGED
@@ -1,9 +1,6 @@
1
1
  /**
2
2
  * Package entry point — public API exported to consumers of this npm package.
3
3
  * This entry point is CLIENT-SAFE and can be imported in React Client Components.
4
- *
5
- * Usage:
6
- * import { ChatWidget, ConfigProvider, useRagChat } from '@retrivora-ai/rag-engine';
7
4
  */
8
5
 
9
6
  // ── React Components ─────────────────────────────────────────
@@ -18,10 +15,9 @@ export { useRagChat } from './hooks/useRagChat';
18
15
 
19
16
  // ── Types (Interfaces/Types only, no Node.js deps) ─────────────
20
17
  export type { RagConfig, VectorDBConfig, LLMConfig, EmbeddingConfig, UIConfig, RAGConfig } from './config/RagConfig';
21
- export type { IVectorDB, VectorMatch, UpsertDocument } from './vectordb/IVectorDB';
18
+ export type { VectorMatch, UpsertDocument } from './types';
22
19
  export type { ILLMProvider, ChatMessage, ChatOptions, EmbedOptions } from './llm/ILLMProvider';
23
- export type { IngestDocument, ChatResponse } from './rag/RAGPipeline';
20
+ export type { IngestDocument, ChatResponse } from './types';
24
21
  export type { Chunk, ChunkOptions } from './rag/DocumentChunker';
25
22
  export type { ClientConfig } from './components/ConfigProvider';
26
23
  export type { RagMessage, UseRagChatOptions, UseRagChatReturn } from './hooks/useRagChat';
27
-
@@ -0,0 +1,61 @@
1
+ import { VectorDBConfig } from '../../config/RagConfig';
2
+ import { VectorMatch, UpsertDocument } from '../../types';
3
+
4
+ /**
5
+ * BaseVectorProvider — Abstract base class for all vector database providers.
6
+ * Each provider (Pinecone, Milvus, Redis, etc.) must extend this class.
7
+ */
8
+ export abstract class BaseVectorProvider {
9
+ protected readonly config: VectorDBConfig;
10
+ protected readonly indexName: string;
11
+
12
+ constructor(config: VectorDBConfig) {
13
+ this.config = config;
14
+ this.indexName = config.indexName || 'default';
15
+ }
16
+
17
+ /**
18
+ * Initialise the connection (create tables, verify index, etc.)
19
+ */
20
+ abstract initialize(): Promise<void>;
21
+
22
+ /**
23
+ * Insert or update a single vector + content pair.
24
+ */
25
+ abstract upsert(doc: UpsertDocument, namespace?: string): Promise<void>;
26
+
27
+ /**
28
+ * Batch upsert for efficient ingestion of many documents.
29
+ */
30
+ abstract batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void>;
31
+
32
+ /**
33
+ * Find the top-K most similar vectors to the query vector.
34
+ */
35
+ abstract query(
36
+ vector: number[],
37
+ topK: number,
38
+ namespace?: string,
39
+ filter?: Record<string, unknown>
40
+ ): Promise<VectorMatch[]>;
41
+
42
+ /**
43
+ * Delete a stored vector by ID.
44
+ */
45
+ abstract delete(id: string, namespace?: string): Promise<void>;
46
+
47
+ /**
48
+ * Delete all vectors in a namespace (full data reset for a project).
49
+ */
50
+ abstract deleteNamespace(namespace: string): Promise<void>;
51
+
52
+ /**
53
+ * Check if the underlying DB is reachable.
54
+ */
55
+ abstract ping(): Promise<boolean>;
56
+
57
+ /**
58
+ * Gracefully close the connection.
59
+ */
60
+ abstract disconnect(): Promise<void>;
61
+ }
@@ -0,0 +1,94 @@
1
+ import axios, { AxiosInstance } from 'axios';
2
+ import { VectorDBConfig } from '../../config/RagConfig';
3
+ import { BaseVectorProvider } from './BaseVectorProvider';
4
+ import { VectorMatch, UpsertDocument } from '../../types';
5
+
6
+ /**
7
+ * ChromaDBProvider — implementation for ChromaDB using its REST API.
8
+ */
9
+ export class ChromaDBProvider extends BaseVectorProvider {
10
+ private http: AxiosInstance;
11
+ private collectionId: string = '';
12
+
13
+ constructor(config: VectorDBConfig) {
14
+ super(config);
15
+ const opts = config.options as Record<string, unknown>;
16
+ const baseUrl = opts.baseUrl as string;
17
+ if (!baseUrl) throw new Error('[ChromaDBProvider] baseUrl is required');
18
+
19
+ this.http = axios.create({
20
+ baseURL: baseUrl,
21
+ headers: { 'Content-Type': 'application/json' },
22
+ });
23
+ }
24
+
25
+ async initialize(): Promise<void> {
26
+ // Get or create collection
27
+ const { data } = await this.http.get(`/api/v1/collections/${this.indexName}`);
28
+ this.collectionId = data.id;
29
+ }
30
+
31
+ async upsert(doc: UpsertDocument, namespace?: string): Promise<void> {
32
+ await this.batchUpsert([doc], namespace);
33
+ }
34
+
35
+ async batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void> {
36
+ const payload = {
37
+ ids: docs.map(d => d.id),
38
+ embeddings: docs.map(d => d.vector),
39
+ documents: docs.map(d => d.content),
40
+ metadatas: docs.map(d => ({
41
+ ...(d.metadata || {}),
42
+ ...(namespace ? { namespace } : {}),
43
+ })),
44
+ };
45
+ await this.http.post(`/api/v1/collections/${this.collectionId}/add`, payload);
46
+ }
47
+
48
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
+ async query(vector: number[], topK: number, namespace?: string, _filter?: Record<string, unknown>): Promise<VectorMatch[]> {
50
+ const payload = {
51
+ query_embeddings: [vector],
52
+ n_results: topK,
53
+ where: namespace ? { namespace: { $eq: namespace } } : undefined,
54
+ };
55
+ const { data } = await this.http.post(`/api/v1/collections/${this.collectionId}/query`, payload);
56
+
57
+ const matches: VectorMatch[] = [];
58
+ if (data.ids && data.ids[0]) {
59
+ for (let i = 0; i < data.ids[0].length; i++) {
60
+ matches.push({
61
+ id: data.ids[0][i],
62
+ score: data.distances ? 1 - data.distances[0][i] : 0,
63
+ content: data.documents ? data.documents[0][i] : '',
64
+ metadata: data.metadatas ? data.metadatas[0][i] : {},
65
+ });
66
+ }
67
+ }
68
+ return matches;
69
+ }
70
+
71
+ async delete(id: string, _namespace?: string): Promise<void> {
72
+ await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
73
+ ids: [id],
74
+ where: _namespace ? { namespace: { $eq: _namespace } } : undefined,
75
+ });
76
+ }
77
+
78
+ async deleteNamespace(_namespace: string): Promise<void> {
79
+ await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
80
+ where: { namespace: { $eq: _namespace } },
81
+ });
82
+ }
83
+
84
+ async ping(): Promise<boolean> {
85
+ try {
86
+ await this.http.get('/api/v1/heartbeat');
87
+ return true;
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+
93
+ async disconnect(): Promise<void> {}
94
+ }
@@ -0,0 +1,100 @@
1
+ import axios, { AxiosInstance } from 'axios';
2
+ import { VectorDBConfig } from '../../config/RagConfig';
3
+ import { BaseVectorProvider } from './BaseVectorProvider';
4
+ import { VectorMatch, UpsertDocument } from '../../types';
5
+
6
+ /**
7
+ * MilvusProvider — implementation for Milvus using its REST API.
8
+ */
9
+ export class MilvusProvider extends BaseVectorProvider {
10
+ private http: AxiosInstance;
11
+
12
+ constructor(config: VectorDBConfig) {
13
+ super(config);
14
+ const opts = config.options as Record<string, unknown>;
15
+ const baseUrl = (opts.baseUrl as string) || (opts.uri as string);
16
+ if (!baseUrl) throw new Error('[MilvusProvider] baseUrl/uri is required');
17
+
18
+ this.http = axios.create({
19
+ baseURL: baseUrl,
20
+ headers: {
21
+ 'Content-Type': 'application/json',
22
+ ...(opts.headers as Record<string, string> || {}),
23
+ ...(opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {}),
24
+ },
25
+ });
26
+ }
27
+
28
+ async initialize(): Promise<void> {
29
+ await this.ping();
30
+ }
31
+
32
+ async upsert(doc: UpsertDocument, namespace?: string): Promise<void> {
33
+ const payload = {
34
+ collectionName: this.indexName,
35
+ data: [{
36
+ vector: doc.vector,
37
+ content: doc.content,
38
+ metadata: doc.metadata || {},
39
+ ...(namespace ? { namespace } : {}),
40
+ }],
41
+ };
42
+ await this.http.post('/v1/vector/upsert', payload);
43
+ }
44
+
45
+ async batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void> {
46
+ const payload = {
47
+ collectionName: this.indexName,
48
+ data: docs.map(doc => ({
49
+ vector: doc.vector,
50
+ content: doc.content,
51
+ metadata: doc.metadata || {},
52
+ ...(namespace ? { namespace } : {}),
53
+ })),
54
+ };
55
+ await this.http.post('/v1/vector/upsert', payload);
56
+ }
57
+
58
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
59
+ async query(vector: number[], topK: number, namespace?: string, _filter?: Record<string, unknown>): Promise<VectorMatch[]> {
60
+ const payload = {
61
+ collectionName: this.indexName,
62
+ vector: vector,
63
+ limit: topK,
64
+ filter: namespace ? `namespace == "${namespace}"` : undefined,
65
+ outputFields: ['content', 'metadata'],
66
+ };
67
+ const { data } = await this.http.post('/v1/vector/search', payload);
68
+ return (data.data || []).map((res: Record<string, unknown>) => ({
69
+ id: String(res['id']),
70
+ score: res['distance'] as number,
71
+ content: res['content'] as string,
72
+ metadata: res['metadata'] as Record<string, unknown>,
73
+ }));
74
+ }
75
+
76
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
77
+ async delete(id: string, _namespace?: string): Promise<void> {
78
+ await this.http.post('/v1/vector/delete', {
79
+ collectionName: this.indexName,
80
+ id: id,
81
+ });
82
+ }
83
+
84
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
+ async deleteNamespace(_namespace: string): Promise<void> {
86
+ // Milvus doesn't have a direct "delete namespace" REST API easily
87
+ // usually handled by deleting with a filter
88
+ }
89
+
90
+ async ping(): Promise<boolean> {
91
+ try {
92
+ await this.http.get('/v1/health');
93
+ return true;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+
99
+ async disconnect(): Promise<void> {}
100
+ }
@@ -0,0 +1,118 @@
1
+ import { MongoClient, Collection, Db } from 'mongodb';
2
+ import { VectorDBConfig } from '../../config/RagConfig';
3
+ import { BaseVectorProvider } from './BaseVectorProvider';
4
+ import { VectorMatch, UpsertDocument } from '../../types';
5
+
6
+ /**
7
+ * MongoDBProvider — MongoDB Atlas Vector Search implementation.
8
+ */
9
+ export class MongoDBProvider extends BaseVectorProvider {
10
+ private client: MongoClient;
11
+ private db?: Db;
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ private collection?: Collection<any>;
14
+ private dbName: string;
15
+ private collectionName: string;
16
+ private embeddingKey: string;
17
+ private contentKey: string;
18
+ private metadataKey: string;
19
+
20
+ constructor(config: VectorDBConfig) {
21
+ super(config);
22
+ const opts = config.options as Record<string, unknown>;
23
+ if (!opts.uri || !opts.database || !opts.collection) {
24
+ throw new Error('[MongoDBProvider] options uri, database, and collection are required.');
25
+ }
26
+ this.client = new MongoClient(opts.uri as string);
27
+ this.dbName = opts.database as string;
28
+ this.collectionName = opts.collection as string;
29
+ this.embeddingKey = (opts.embeddingKey as string) || 'embedding';
30
+ this.contentKey = (opts.contentKey as string) || 'content';
31
+ this.metadataKey = (opts.metadataKey as string) || 'metadata';
32
+ }
33
+
34
+ async initialize(): Promise<void> {
35
+ await this.client.connect();
36
+ this.db = this.client.db(this.dbName);
37
+ this.collection = this.db.collection(this.collectionName);
38
+ }
39
+
40
+ async upsert(doc: UpsertDocument, namespace?: string): Promise<void> {
41
+ const document = {
42
+ _id: doc.id,
43
+ [this.embeddingKey]: doc.vector,
44
+ [this.contentKey]: doc.content,
45
+ [this.metadataKey]: doc.metadata || {},
46
+ ...(namespace ? { namespace } : {}),
47
+ };
48
+ await this.collection!.updateOne({ _id: doc.id }, { $set: document }, { upsert: true });
49
+ }
50
+
51
+ async batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void> {
52
+ const operations = docs.map((doc) => ({
53
+ updateOne: {
54
+ filter: { _id: doc.id },
55
+ update: {
56
+ $set: {
57
+ [this.embeddingKey]: doc.vector,
58
+ [this.contentKey]: doc.content,
59
+ [this.metadataKey]: doc.metadata || {},
60
+ ...(namespace ? { namespace } : {}),
61
+ },
62
+ },
63
+ upsert: true,
64
+ },
65
+ }));
66
+ await this.collection!.bulkWrite(operations);
67
+ }
68
+
69
+ async query(vector: number[], topK: number, namespace?: string, filter?: Record<string, unknown>): Promise<VectorMatch[]> {
70
+ const pipeline: Record<string, unknown>[] = [
71
+ {
72
+ $vectorSearch: {
73
+ index: this.config.options.indexName || 'vector_index',
74
+ path: this.embeddingKey,
75
+ queryVector: vector,
76
+ numCandidates: Math.max(topK * 10, 100),
77
+ limit: topK,
78
+ ...(filter || namespace ? { filter: { ...(filter || {}), ...(namespace ? { namespace } : {}) } } : {}),
79
+ },
80
+ },
81
+ {
82
+ $project: {
83
+ score: { $meta: 'vectorSearchScore' },
84
+ content: `$${this.contentKey}`,
85
+ metadata: `$${this.metadataKey}`,
86
+ },
87
+ },
88
+ ];
89
+ const results = await this.collection!.aggregate(pipeline).toArray();
90
+ return results.map((res: Record<string, unknown>) => ({
91
+ id: res['_id']!.toString(),
92
+ score: res['score'] as number,
93
+ content: res['content'] as string,
94
+ metadata: res['metadata'] as Record<string, unknown>,
95
+ }));
96
+ }
97
+
98
+ async delete(id: string, namespace?: string): Promise<void> {
99
+ await this.collection!.deleteOne({ _id: id, ...(namespace ? { namespace } : {}) });
100
+ }
101
+
102
+ async deleteNamespace(namespace: string): Promise<void> {
103
+ await this.collection!.deleteMany({ namespace });
104
+ }
105
+
106
+ async ping(): Promise<boolean> {
107
+ try {
108
+ await this.db!.command({ ping: 1 });
109
+ return true;
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+
115
+ async disconnect(): Promise<void> {
116
+ await this.client.close();
117
+ }
118
+ }