@retrivora-ai/rag-engine 1.0.9 → 1.1.1

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  QdrantProvider
3
- } from "./chunk-NT5VP7MT.mjs";
3
+ } from "./chunk-63HITIWC.mjs";
4
4
  import "./chunk-IMP6FUCY.mjs";
5
5
  import "./chunk-X4TOT24V.mjs";
6
6
  export {
@@ -0,0 +1,245 @@
1
+ import {
2
+ BaseVectorProvider
3
+ } from "./chunk-IMP6FUCY.mjs";
4
+ import {
5
+ __spreadValues
6
+ } from "./chunk-X4TOT24V.mjs";
7
+
8
+ // src/providers/vectordb/QdrantProvider.ts
9
+ import axios from "axios";
10
+ import crypto from "crypto";
11
+ var QdrantProvider = class extends BaseVectorProvider {
12
+ constructor(config) {
13
+ super(config);
14
+ this.schemaDiscovered = false;
15
+ const opts = config.options;
16
+ const baseUrl = opts.baseUrl;
17
+ if (!baseUrl) throw new Error("[QdrantProvider] baseUrl is required");
18
+ this.contentField = opts.contentField || "content";
19
+ this.metadataField = opts.metadataField || "metadata";
20
+ this.isFlatPayload = !!opts.flatPayload;
21
+ this.http = axios.create({
22
+ baseURL: baseUrl,
23
+ timeout: 15e3,
24
+ // 15s timeout for vector DB operations
25
+ headers: __spreadValues({
26
+ "Content-Type": "application/json"
27
+ }, opts.apiKey ? { "api-key": opts.apiKey } : {})
28
+ });
29
+ }
30
+ async initialize() {
31
+ if (this.schemaDiscovered) return;
32
+ await this.ping();
33
+ await this.ensureCollection();
34
+ console.log(`[QdrantProvider] \u{1F50D} Discovering schema for collection "${this.indexName}"...`);
35
+ const discoveredFields = await this.discoverSchema();
36
+ const opts = this.config.options;
37
+ const configFields = opts.filterableFields || [];
38
+ const allFields = [.../* @__PURE__ */ new Set([...discoveredFields, ...configFields])];
39
+ if (allFields.length > 0) {
40
+ console.log(`[QdrantProvider] \u{1F6E0} Ensuring indexes for ${allFields.length} discovered fields...`);
41
+ await Promise.all(
42
+ allFields.map(async (fieldInfo) => {
43
+ const [fieldName, schemaType] = fieldInfo.split(":");
44
+ return this.ensureIndex(fieldName, schemaType || "keyword");
45
+ })
46
+ );
47
+ } else {
48
+ console.log(`[QdrantProvider] \u2139\uFE0F No fields discovered for indexing.`);
49
+ }
50
+ this.schemaDiscovered = true;
51
+ }
52
+ /**
53
+ * Samples points from the collection to discover available payload fields.
54
+ */
55
+ async discoverSchema() {
56
+ var _a;
57
+ try {
58
+ const { data } = await this.http.post(`/collections/${this.indexName}/points/scroll`, {
59
+ limit: 20,
60
+ with_payload: true,
61
+ with_vector: false
62
+ });
63
+ const points = ((_a = data.result) == null ? void 0 : _a.points) || [];
64
+ const keys = /* @__PURE__ */ new Set();
65
+ for (const point of points) {
66
+ const payload = point.payload || {};
67
+ Object.keys(payload).forEach((k) => {
68
+ if (k !== "namespace" && k !== this.contentField && k !== this.metadataField) {
69
+ keys.add(k);
70
+ }
71
+ });
72
+ if (!this.isFlatPayload && payload[this.metadataField]) {
73
+ const meta = payload[this.metadataField];
74
+ if (typeof meta === "object" && meta !== null) {
75
+ Object.keys(meta).forEach((k) => keys.add(k));
76
+ }
77
+ }
78
+ }
79
+ return Array.from(keys);
80
+ } catch (err) {
81
+ console.warn(`[QdrantProvider] \u26A0\uFE0F Schema discovery failed:`, err instanceof Error ? err.message : String(err));
82
+ return [];
83
+ }
84
+ }
85
+ /**
86
+ * Ensures the collection exists. Creates it if missing.
87
+ */
88
+ async ensureCollection() {
89
+ var _a;
90
+ try {
91
+ await this.http.get(`/collections/${this.indexName}`);
92
+ console.log(`[QdrantProvider] \u2705 Collection "${this.indexName}" already exists.`);
93
+ } catch (err) {
94
+ if (axios.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
95
+ const opts = this.config.options;
96
+ const dimensionsForCreate = opts.dimensions || 1536;
97
+ console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
98
+ await this.http.put(`/collections/${this.indexName}`, {
99
+ vectors: {
100
+ size: dimensionsForCreate,
101
+ distance: "Cosine"
102
+ }
103
+ });
104
+ console.log(`[QdrantProvider] \u2705 Created collection "${this.indexName}"`);
105
+ } else {
106
+ throw err;
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Ensures that a payload field has an index.
112
+ */
113
+ async ensureIndex(fieldName, schema = "keyword") {
114
+ var _a;
115
+ let fullPath = fieldName;
116
+ if (!this.isFlatPayload && !fieldName.includes(".") && fieldName !== "namespace" && fieldName !== this.contentField) {
117
+ fullPath = `${this.metadataField}.${fieldName}`;
118
+ }
119
+ try {
120
+ await this.http.put(`/collections/${this.indexName}/index`, {
121
+ field_name: fullPath,
122
+ field_schema: schema
123
+ });
124
+ console.log(`[QdrantProvider] \u2705 Ensured ${schema} index for "${fullPath}"`);
125
+ } catch (err) {
126
+ let status;
127
+ if (axios.isAxiosError(err)) status = (_a = err.response) == null ? void 0 : _a.status;
128
+ if (status === 409) return;
129
+ console.warn(`[QdrantProvider] \u26A0\uFE0F Could not ensure index for "${fullPath}":`, err instanceof Error ? err.message : String(err));
130
+ }
131
+ }
132
+ async upsert(doc, namespace) {
133
+ await this.batchUpsert([doc], namespace);
134
+ }
135
+ async batchUpsert(docs, namespace) {
136
+ const payload = {
137
+ points: docs.map((doc) => {
138
+ const pointPayload = __spreadValues({
139
+ [this.contentField]: doc.content
140
+ }, namespace ? { namespace } : {});
141
+ if (this.isFlatPayload) {
142
+ Object.assign(pointPayload, doc.metadata || {});
143
+ } else {
144
+ pointPayload[this.metadataField] = doc.metadata || {};
145
+ }
146
+ return {
147
+ id: this.normalizeId(doc.id),
148
+ vector: doc.vector,
149
+ payload: pointPayload
150
+ };
151
+ })
152
+ };
153
+ await this.http.put(`/collections/${this.indexName}/points`, payload);
154
+ }
155
+ async query(vector, topK, namespace, _filter) {
156
+ const must = [];
157
+ if (namespace) {
158
+ must.push({ key: "namespace", match: { value: namespace } });
159
+ }
160
+ const sanitizedFilter = this.sanitizeFilter(_filter);
161
+ for (const [key, value] of Object.entries(sanitizedFilter)) {
162
+ let filterKey = key;
163
+ if (!this.isFlatPayload && !key.includes(".") && key !== "namespace" && key !== this.contentField) {
164
+ filterKey = `${this.metadataField}.${key}`;
165
+ }
166
+ if (Array.isArray(value)) {
167
+ must.push({ key: filterKey, match: { any: value } });
168
+ } else {
169
+ must.push({ key: filterKey, match: { value } });
170
+ }
171
+ }
172
+ const payload = {
173
+ vector,
174
+ limit: topK,
175
+ with_payload: true,
176
+ params: {
177
+ hnsw_ef: this.config.options.efSearch || Math.max(topK * 20, 128),
178
+ exact: false
179
+ },
180
+ filter: must.length > 0 ? { must } : void 0
181
+ };
182
+ const { data } = await this.http.post(`/collections/${this.indexName}/points/search`, payload);
183
+ const results = (data.result || []).map((res) => {
184
+ const p = res.payload || {};
185
+ let content = p[this.contentField] || "";
186
+ if (!content) {
187
+ const stringFields = Object.entries(p).filter(([_, v]) => typeof v === "string").map(([k, v]) => ({ key: k, val: v }));
188
+ if (stringFields.length > 0) {
189
+ const bestMatch = stringFields.sort((a, b) => b.val.length - a.val.length)[0];
190
+ content = bestMatch.val;
191
+ } else {
192
+ content = JSON.stringify(p);
193
+ }
194
+ }
195
+ let metadata = {};
196
+ if (this.isFlatPayload) {
197
+ metadata = __spreadValues({}, p);
198
+ delete metadata[this.contentField];
199
+ delete metadata["namespace"];
200
+ } else {
201
+ metadata = p[this.metadataField] || p;
202
+ }
203
+ return {
204
+ id: res.id,
205
+ score: res.score,
206
+ content,
207
+ metadata
208
+ };
209
+ });
210
+ return results;
211
+ }
212
+ async delete(id, _namespace) {
213
+ await this.http.post(`/collections/${this.indexName}/points/delete`, {
214
+ points: [this.normalizeId(id)]
215
+ });
216
+ }
217
+ async deleteNamespace(_namespace) {
218
+ await this.http.post(`/collections/${this.indexName}/points/delete`, {
219
+ filter: {
220
+ must: [{ key: "namespace", match: { value: _namespace } }]
221
+ }
222
+ });
223
+ }
224
+ async ping() {
225
+ try {
226
+ await this.http.get("/healthz");
227
+ return true;
228
+ } catch (e) {
229
+ return false;
230
+ }
231
+ }
232
+ normalizeId(id) {
233
+ if (typeof id === "number") return id;
234
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
235
+ if (uuidRegex.test(id)) return id.toLowerCase();
236
+ const hash = crypto.createHash("md5").update(id).digest("hex");
237
+ return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
238
+ }
239
+ async disconnect() {
240
+ }
241
+ };
242
+
243
+ export {
244
+ QdrantProvider
245
+ };
@@ -333,6 +333,53 @@ ${context}`
333
333
  });
334
334
  return (_h = (_g = (_f = completion.choices[0]) == null ? void 0 : _f.message) == null ? void 0 : _g.content) != null ? _h : "";
335
335
  }
336
+ chatStream(messages, context, options) {
337
+ return __asyncGenerator(this, null, function* () {
338
+ var _a, _b, _c, _d, _e, _f, _g;
339
+ const systemContent = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
340
+
341
+ Context:
342
+ ${context}`;
343
+ const systemMessage = {
344
+ role: "system",
345
+ content: systemContent.includes("{{context}}") ? systemContent.replace("{{context}}", context) : `${systemContent}
346
+
347
+ Context:
348
+ ${context}`
349
+ };
350
+ const formattedMessages = [
351
+ systemMessage,
352
+ ...messages.map((m) => ({
353
+ role: m.role,
354
+ content: m.content
355
+ }))
356
+ ];
357
+ const stream = yield new __await(this.client.chat.completions.create({
358
+ model: this.llmConfig.model,
359
+ messages: formattedMessages,
360
+ max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
361
+ temperature: (_e = (_d = options == null ? void 0 : options.temperature) != null ? _d : this.llmConfig.temperature) != null ? _e : 0.7,
362
+ stop: options == null ? void 0 : options.stop,
363
+ stream: true
364
+ }));
365
+ try {
366
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
367
+ const chunk = temp.value;
368
+ const content = ((_g = (_f = chunk.choices[0]) == null ? void 0 : _f.delta) == null ? void 0 : _g.content) || "";
369
+ if (content) yield content;
370
+ }
371
+ } catch (temp) {
372
+ error = [temp];
373
+ } finally {
374
+ try {
375
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
376
+ } finally {
377
+ if (error)
378
+ throw error[0];
379
+ }
380
+ }
381
+ });
382
+ }
336
383
  async embed(text, options) {
337
384
  const results = await this.batchEmbed([text], options);
338
385
  return results[0];
@@ -438,6 +485,47 @@ ${context}`;
438
485
  const block = response.content[0];
439
486
  return block.type === "text" ? block.text : "";
440
487
  }
488
+ chatStream(messages, context, options) {
489
+ return __asyncGenerator(this, null, function* () {
490
+ var _a, _b, _c;
491
+ const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the context below to answer the user's question accurately.
492
+
493
+ Context:
494
+ ${context}`;
495
+ const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
496
+
497
+ Context:
498
+ ${context}`;
499
+ const anthropicMessages = messages.map((m) => ({
500
+ role: m.role === "assistant" ? "assistant" : "user",
501
+ content: m.content
502
+ }));
503
+ const stream = yield new __await(this.client.messages.create({
504
+ model: this.llmConfig.model,
505
+ max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
506
+ system,
507
+ messages: anthropicMessages,
508
+ stream: true
509
+ }));
510
+ try {
511
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
512
+ const chunk = temp.value;
513
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
514
+ yield chunk.delta.text;
515
+ }
516
+ }
517
+ } catch (temp) {
518
+ error = [temp];
519
+ } finally {
520
+ try {
521
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
522
+ } finally {
523
+ if (error)
524
+ throw error[0];
525
+ }
526
+ }
527
+ });
528
+ }
441
529
  async embed(text, options) {
442
530
  void text;
443
531
  void options;
@@ -543,7 +631,7 @@ var OllamaProvider = class {
543
631
  }
544
632
  chatStream(messages, context, options) {
545
633
  return __asyncGenerator(this, null, function* () {
546
- var _a, _b, _c, _d, _e;
634
+ var _a, _b, _c, _d, _e, _f;
547
635
  const system = this.buildSystemPrompt(context);
548
636
  const response = yield new __await(this.http.post("/api/chat", {
549
637
  model: this.llmConfig.model,
@@ -557,11 +645,15 @@ var OllamaProvider = class {
557
645
  ...messages.map((m) => ({ role: m.role, content: m.content }))
558
646
  ]
559
647
  }, { responseType: "stream" }));
648
+ let lineBuffer = "";
560
649
  try {
561
650
  for (var iter = __forAwait(response.data), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
562
651
  const chunk = temp.value;
563
- const lines = chunk.toString().split("\n").filter(Boolean);
652
+ lineBuffer += chunk.toString();
653
+ const lines = lineBuffer.split("\n");
654
+ lineBuffer = lines.pop() || "";
564
655
  for (const line of lines) {
656
+ if (!line.trim()) continue;
565
657
  try {
566
658
  const json = JSON.parse(line);
567
659
  if ((_e = json.message) == null ? void 0 : _e.content) {
@@ -569,6 +661,7 @@ var OllamaProvider = class {
569
661
  }
570
662
  if (json.done) return;
571
663
  } catch (e) {
664
+ console.warn("[OllamaProvider] Failed to parse streaming line:", line);
572
665
  }
573
666
  }
574
667
  }
@@ -582,6 +675,13 @@ var OllamaProvider = class {
582
675
  throw error[0];
583
676
  }
584
677
  }
678
+ if (lineBuffer.trim()) {
679
+ try {
680
+ const json = JSON.parse(lineBuffer);
681
+ if ((_f = json.message) == null ? void 0 : _f.content) yield json.message.content;
682
+ } catch (e) {
683
+ }
684
+ }
585
685
  });
586
686
  }
587
687
  buildSystemPrompt(context) {
@@ -727,6 +827,49 @@ ${context}`;
727
827
  });
728
828
  return (_f = response.text) != null ? _f : "";
729
829
  }
830
+ chatStream(messages, context, options) {
831
+ return __asyncGenerator(this, null, function* () {
832
+ var _a, _b, _c, _d, _e;
833
+ const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
834
+
835
+ Context:
836
+ ${context}`;
837
+ const systemInstruction = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
838
+
839
+ Context:
840
+ ${context}`;
841
+ const geminiMessages = messages.map((m) => ({
842
+ role: m.role === "assistant" ? "model" : "user",
843
+ parts: [{ text: m.content }]
844
+ }));
845
+ const result = yield new __await(this.client.models.generateContentStream({
846
+ model: this.llmConfig.model,
847
+ contents: geminiMessages,
848
+ config: {
849
+ systemInstruction,
850
+ temperature: (_c = (_b = options == null ? void 0 : options.temperature) != null ? _b : this.llmConfig.temperature) != null ? _c : 0.7,
851
+ maxOutputTokens: (_e = (_d = options == null ? void 0 : options.maxTokens) != null ? _d : this.llmConfig.maxTokens) != null ? _e : 1024,
852
+ stopSequences: options == null ? void 0 : options.stop
853
+ }
854
+ }));
855
+ try {
856
+ for (var iter = __forAwait(result.stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
857
+ const chunk = temp.value;
858
+ const text = chunk.text;
859
+ if (text) yield text;
860
+ }
861
+ } catch (temp) {
862
+ error = [temp];
863
+ } finally {
864
+ try {
865
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
866
+ } finally {
867
+ if (error)
868
+ throw error[0];
869
+ }
870
+ }
871
+ });
872
+ }
730
873
  async embed(text, options) {
731
874
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
732
875
  const model = this.sanitizeModel(
@@ -1106,7 +1249,7 @@ var ProviderRegistry = class {
1106
1249
  return MilvusProvider;
1107
1250
  }
1108
1251
  case "qdrant": {
1109
- const { QdrantProvider } = await import("./QdrantProvider-NGFXVDCF.mjs");
1252
+ const { QdrantProvider } = await import("./QdrantProvider-NMXOULCU.mjs");
1110
1253
  return QdrantProvider;
1111
1254
  }
1112
1255
  case "chromadb": {
@@ -2273,6 +2416,7 @@ var Pipeline = class {
2273
2416
  }
2274
2417
  const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
2275
2418
  const filter = QueryProcessor.buildQueryFilter(question, hints);
2419
+ console.log(`[Pipeline] \u{1F9E9} Extracted filters:`, JSON.stringify(filter));
2276
2420
  const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
2277
2421
  namespace: ns,
2278
2422
  topK: topK * 2,