@retrivora-ai/rag-engine 1.1.0 → 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-65S4BQL2.mjs";
3
+ } from "./chunk-63HITIWC.mjs";
4
4
  import "./chunk-IMP6FUCY.mjs";
5
5
  import "./chunk-X4TOT24V.mjs";
6
6
  export {
@@ -11,6 +11,7 @@ import crypto from "crypto";
11
11
  var QdrantProvider = class extends BaseVectorProvider {
12
12
  constructor(config) {
13
13
  super(config);
14
+ this.schemaDiscovered = false;
14
15
  const opts = config.options;
15
16
  const baseUrl = opts.baseUrl;
16
17
  if (!baseUrl) throw new Error("[QdrantProvider] baseUrl is required");
@@ -19,12 +20,15 @@ var QdrantProvider = class extends BaseVectorProvider {
19
20
  this.isFlatPayload = !!opts.flatPayload;
20
21
  this.http = axios.create({
21
22
  baseURL: baseUrl,
23
+ timeout: 15e3,
24
+ // 15s timeout for vector DB operations
22
25
  headers: __spreadValues({
23
26
  "Content-Type": "application/json"
24
27
  }, opts.apiKey ? { "api-key": opts.apiKey } : {})
25
28
  });
26
29
  }
27
30
  async initialize() {
31
+ if (this.schemaDiscovered) return;
28
32
  await this.ping();
29
33
  await this.ensureCollection();
30
34
  console.log(`[QdrantProvider] \u{1F50D} Discovering schema for collection "${this.indexName}"...`);
@@ -34,13 +38,16 @@ var QdrantProvider = class extends BaseVectorProvider {
34
38
  const allFields = [.../* @__PURE__ */ new Set([...discoveredFields, ...configFields])];
35
39
  if (allFields.length > 0) {
36
40
  console.log(`[QdrantProvider] \u{1F6E0} Ensuring indexes for ${allFields.length} discovered fields...`);
37
- for (const fieldInfo of allFields) {
38
- const [fieldName, schemaType] = fieldInfo.split(":");
39
- await this.ensureIndex(fieldName, schemaType || "keyword");
40
- }
41
+ await Promise.all(
42
+ allFields.map(async (fieldInfo) => {
43
+ const [fieldName, schemaType] = fieldInfo.split(":");
44
+ return this.ensureIndex(fieldName, schemaType || "keyword");
45
+ })
46
+ );
41
47
  } else {
42
48
  console.log(`[QdrantProvider] \u2139\uFE0F No fields discovered for indexing.`);
43
49
  }
50
+ this.schemaDiscovered = true;
44
51
  }
45
52
  /**
46
53
  * Samples points from the collection to discover available payload fields.
@@ -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-WWXFX2XF.mjs");
1252
+ const { QdrantProvider } = await import("./QdrantProvider-NMXOULCU.mjs");
1110
1253
  return QdrantProvider;
1111
1254
  }
1112
1255
  case "chromadb": {
@@ -842,6 +842,7 @@ var init_QdrantProvider = __esm({
842
842
  QdrantProvider = class extends BaseVectorProvider {
843
843
  constructor(config) {
844
844
  super(config);
845
+ this.schemaDiscovered = false;
845
846
  const opts = config.options;
846
847
  const baseUrl = opts.baseUrl;
847
848
  if (!baseUrl) throw new Error("[QdrantProvider] baseUrl is required");
@@ -850,12 +851,15 @@ var init_QdrantProvider = __esm({
850
851
  this.isFlatPayload = !!opts.flatPayload;
851
852
  this.http = import_axios4.default.create({
852
853
  baseURL: baseUrl,
854
+ timeout: 15e3,
855
+ // 15s timeout for vector DB operations
853
856
  headers: __spreadValues({
854
857
  "Content-Type": "application/json"
855
858
  }, opts.apiKey ? { "api-key": opts.apiKey } : {})
856
859
  });
857
860
  }
858
861
  async initialize() {
862
+ if (this.schemaDiscovered) return;
859
863
  await this.ping();
860
864
  await this.ensureCollection();
861
865
  console.log(`[QdrantProvider] \u{1F50D} Discovering schema for collection "${this.indexName}"...`);
@@ -865,13 +869,16 @@ var init_QdrantProvider = __esm({
865
869
  const allFields = [.../* @__PURE__ */ new Set([...discoveredFields, ...configFields])];
866
870
  if (allFields.length > 0) {
867
871
  console.log(`[QdrantProvider] \u{1F6E0} Ensuring indexes for ${allFields.length} discovered fields...`);
868
- for (const fieldInfo of allFields) {
869
- const [fieldName, schemaType] = fieldInfo.split(":");
870
- await this.ensureIndex(fieldName, schemaType || "keyword");
871
- }
872
+ await Promise.all(
873
+ allFields.map(async (fieldInfo) => {
874
+ const [fieldName, schemaType] = fieldInfo.split(":");
875
+ return this.ensureIndex(fieldName, schemaType || "keyword");
876
+ })
877
+ );
872
878
  } else {
873
879
  console.log(`[QdrantProvider] \u2139\uFE0F No fields discovered for indexing.`);
874
880
  }
881
+ this.schemaDiscovered = true;
875
882
  }
876
883
  /**
877
884
  * Samples points from the collection to discover available payload fields.
@@ -1932,6 +1939,53 @@ ${context}`
1932
1939
  });
1933
1940
  return (_h = (_g = (_f = completion.choices[0]) == null ? void 0 : _f.message) == null ? void 0 : _g.content) != null ? _h : "";
1934
1941
  }
1942
+ chatStream(messages, context, options) {
1943
+ return __asyncGenerator(this, null, function* () {
1944
+ var _a, _b, _c, _d, _e, _f, _g;
1945
+ const systemContent = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
1946
+
1947
+ Context:
1948
+ ${context}`;
1949
+ const systemMessage = {
1950
+ role: "system",
1951
+ content: systemContent.includes("{{context}}") ? systemContent.replace("{{context}}", context) : `${systemContent}
1952
+
1953
+ Context:
1954
+ ${context}`
1955
+ };
1956
+ const formattedMessages = [
1957
+ systemMessage,
1958
+ ...messages.map((m) => ({
1959
+ role: m.role,
1960
+ content: m.content
1961
+ }))
1962
+ ];
1963
+ const stream = yield new __await(this.client.chat.completions.create({
1964
+ model: this.llmConfig.model,
1965
+ messages: formattedMessages,
1966
+ max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
1967
+ temperature: (_e = (_d = options == null ? void 0 : options.temperature) != null ? _d : this.llmConfig.temperature) != null ? _e : 0.7,
1968
+ stop: options == null ? void 0 : options.stop,
1969
+ stream: true
1970
+ }));
1971
+ try {
1972
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
1973
+ const chunk = temp.value;
1974
+ const content = ((_g = (_f = chunk.choices[0]) == null ? void 0 : _f.delta) == null ? void 0 : _g.content) || "";
1975
+ if (content) yield content;
1976
+ }
1977
+ } catch (temp) {
1978
+ error = [temp];
1979
+ } finally {
1980
+ try {
1981
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
1982
+ } finally {
1983
+ if (error)
1984
+ throw error[0];
1985
+ }
1986
+ }
1987
+ });
1988
+ }
1935
1989
  async embed(text, options) {
1936
1990
  const results = await this.batchEmbed([text], options);
1937
1991
  return results[0];
@@ -2037,6 +2091,47 @@ ${context}`;
2037
2091
  const block = response.content[0];
2038
2092
  return block.type === "text" ? block.text : "";
2039
2093
  }
2094
+ chatStream(messages, context, options) {
2095
+ return __asyncGenerator(this, null, function* () {
2096
+ var _a, _b, _c;
2097
+ const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the context below to answer the user's question accurately.
2098
+
2099
+ Context:
2100
+ ${context}`;
2101
+ const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
2102
+
2103
+ Context:
2104
+ ${context}`;
2105
+ const anthropicMessages = messages.map((m) => ({
2106
+ role: m.role === "assistant" ? "assistant" : "user",
2107
+ content: m.content
2108
+ }));
2109
+ const stream = yield new __await(this.client.messages.create({
2110
+ model: this.llmConfig.model,
2111
+ max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
2112
+ system,
2113
+ messages: anthropicMessages,
2114
+ stream: true
2115
+ }));
2116
+ try {
2117
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2118
+ const chunk = temp.value;
2119
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
2120
+ yield chunk.delta.text;
2121
+ }
2122
+ }
2123
+ } catch (temp) {
2124
+ error = [temp];
2125
+ } finally {
2126
+ try {
2127
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
2128
+ } finally {
2129
+ if (error)
2130
+ throw error[0];
2131
+ }
2132
+ }
2133
+ });
2134
+ }
2040
2135
  async embed(text, options) {
2041
2136
  void text;
2042
2137
  void options;
@@ -2142,7 +2237,7 @@ var OllamaProvider = class {
2142
2237
  }
2143
2238
  chatStream(messages, context, options) {
2144
2239
  return __asyncGenerator(this, null, function* () {
2145
- var _a, _b, _c, _d, _e;
2240
+ var _a, _b, _c, _d, _e, _f;
2146
2241
  const system = this.buildSystemPrompt(context);
2147
2242
  const response = yield new __await(this.http.post("/api/chat", {
2148
2243
  model: this.llmConfig.model,
@@ -2156,11 +2251,15 @@ var OllamaProvider = class {
2156
2251
  ...messages.map((m) => ({ role: m.role, content: m.content }))
2157
2252
  ]
2158
2253
  }, { responseType: "stream" }));
2254
+ let lineBuffer = "";
2159
2255
  try {
2160
2256
  for (var iter = __forAwait(response.data), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2161
2257
  const chunk = temp.value;
2162
- const lines = chunk.toString().split("\n").filter(Boolean);
2258
+ lineBuffer += chunk.toString();
2259
+ const lines = lineBuffer.split("\n");
2260
+ lineBuffer = lines.pop() || "";
2163
2261
  for (const line of lines) {
2262
+ if (!line.trim()) continue;
2164
2263
  try {
2165
2264
  const json = JSON.parse(line);
2166
2265
  if ((_e = json.message) == null ? void 0 : _e.content) {
@@ -2168,6 +2267,7 @@ var OllamaProvider = class {
2168
2267
  }
2169
2268
  if (json.done) return;
2170
2269
  } catch (e) {
2270
+ console.warn("[OllamaProvider] Failed to parse streaming line:", line);
2171
2271
  }
2172
2272
  }
2173
2273
  }
@@ -2181,6 +2281,13 @@ var OllamaProvider = class {
2181
2281
  throw error[0];
2182
2282
  }
2183
2283
  }
2284
+ if (lineBuffer.trim()) {
2285
+ try {
2286
+ const json = JSON.parse(lineBuffer);
2287
+ if ((_f = json.message) == null ? void 0 : _f.content) yield json.message.content;
2288
+ } catch (e) {
2289
+ }
2290
+ }
2184
2291
  });
2185
2292
  }
2186
2293
  buildSystemPrompt(context) {
@@ -2326,6 +2433,49 @@ ${context}`;
2326
2433
  });
2327
2434
  return (_f = response.text) != null ? _f : "";
2328
2435
  }
2436
+ chatStream(messages, context, options) {
2437
+ return __asyncGenerator(this, null, function* () {
2438
+ var _a, _b, _c, _d, _e;
2439
+ const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
2440
+
2441
+ Context:
2442
+ ${context}`;
2443
+ const systemInstruction = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
2444
+
2445
+ Context:
2446
+ ${context}`;
2447
+ const geminiMessages = messages.map((m) => ({
2448
+ role: m.role === "assistant" ? "model" : "user",
2449
+ parts: [{ text: m.content }]
2450
+ }));
2451
+ const result = yield new __await(this.client.models.generateContentStream({
2452
+ model: this.llmConfig.model,
2453
+ contents: geminiMessages,
2454
+ config: {
2455
+ systemInstruction,
2456
+ temperature: (_c = (_b = options == null ? void 0 : options.temperature) != null ? _b : this.llmConfig.temperature) != null ? _c : 0.7,
2457
+ maxOutputTokens: (_e = (_d = options == null ? void 0 : options.maxTokens) != null ? _d : this.llmConfig.maxTokens) != null ? _e : 1024,
2458
+ stopSequences: options == null ? void 0 : options.stop
2459
+ }
2460
+ }));
2461
+ try {
2462
+ for (var iter = __forAwait(result.stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2463
+ const chunk = temp.value;
2464
+ const text = chunk.text;
2465
+ if (text) yield text;
2466
+ }
2467
+ } catch (temp) {
2468
+ error = [temp];
2469
+ } finally {
2470
+ try {
2471
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
2472
+ } finally {
2473
+ if (error)
2474
+ throw error[0];
2475
+ }
2476
+ }
2477
+ });
2478
+ }
2329
2479
  async embed(text, options) {
2330
2480
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2331
2481
  const model = this.sanitizeModel(
@@ -8,7 +8,7 @@ import {
8
8
  sseFrame,
9
9
  sseMetaFrame,
10
10
  sseTextFrame
11
- } from "../chunk-G2LVK46T.mjs";
11
+ } from "../chunk-JZ4H7EP6.mjs";
12
12
  import "../chunk-YLTMFW4M.mjs";
13
13
  import "../chunk-X4TOT24V.mjs";
14
14
  export {
package/dist/server.d.mts CHANGED
@@ -738,6 +738,7 @@ declare class QdrantProvider extends BaseVectorProvider {
738
738
  private contentField;
739
739
  private metadataField;
740
740
  private isFlatPayload;
741
+ private schemaDiscovered;
741
742
  constructor(config: VectorDBConfig);
742
743
  initialize(): Promise<void>;
743
744
  /**
@@ -892,6 +893,7 @@ declare class OpenAIProvider implements ILLMProvider {
892
893
  static getValidator(): IProviderValidator;
893
894
  static getHealthChecker(): IProviderHealthChecker;
894
895
  chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
896
+ chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
895
897
  embed(text: string, options?: EmbedOptions): Promise<number[]>;
896
898
  batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
897
899
  ping(): Promise<boolean>;
@@ -909,6 +911,7 @@ declare class AnthropicProvider implements ILLMProvider {
909
911
  static getValidator(): IProviderValidator;
910
912
  static getHealthChecker(): IProviderHealthChecker;
911
913
  chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
914
+ chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
912
915
  embed(text: string, options?: EmbedOptions): Promise<number[]>;
913
916
  batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
914
917
  ping(): Promise<boolean>;
package/dist/server.d.ts CHANGED
@@ -738,6 +738,7 @@ declare class QdrantProvider extends BaseVectorProvider {
738
738
  private contentField;
739
739
  private metadataField;
740
740
  private isFlatPayload;
741
+ private schemaDiscovered;
741
742
  constructor(config: VectorDBConfig);
742
743
  initialize(): Promise<void>;
743
744
  /**
@@ -892,6 +893,7 @@ declare class OpenAIProvider implements ILLMProvider {
892
893
  static getValidator(): IProviderValidator;
893
894
  static getHealthChecker(): IProviderHealthChecker;
894
895
  chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
896
+ chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
895
897
  embed(text: string, options?: EmbedOptions): Promise<number[]>;
896
898
  batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
897
899
  ping(): Promise<boolean>;
@@ -909,6 +911,7 @@ declare class AnthropicProvider implements ILLMProvider {
909
911
  static getValidator(): IProviderValidator;
910
912
  static getHealthChecker(): IProviderHealthChecker;
911
913
  chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
914
+ chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
912
915
  embed(text: string, options?: EmbedOptions): Promise<number[]>;
913
916
  batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
914
917
  ping(): Promise<boolean>;
package/dist/server.js CHANGED
@@ -854,6 +854,7 @@ var init_QdrantProvider = __esm({
854
854
  QdrantProvider = class extends BaseVectorProvider {
855
855
  constructor(config) {
856
856
  super(config);
857
+ this.schemaDiscovered = false;
857
858
  const opts = config.options;
858
859
  const baseUrl = opts.baseUrl;
859
860
  if (!baseUrl) throw new Error("[QdrantProvider] baseUrl is required");
@@ -862,12 +863,15 @@ var init_QdrantProvider = __esm({
862
863
  this.isFlatPayload = !!opts.flatPayload;
863
864
  this.http = import_axios4.default.create({
864
865
  baseURL: baseUrl,
866
+ timeout: 15e3,
867
+ // 15s timeout for vector DB operations
865
868
  headers: __spreadValues({
866
869
  "Content-Type": "application/json"
867
870
  }, opts.apiKey ? { "api-key": opts.apiKey } : {})
868
871
  });
869
872
  }
870
873
  async initialize() {
874
+ if (this.schemaDiscovered) return;
871
875
  await this.ping();
872
876
  await this.ensureCollection();
873
877
  console.log(`[QdrantProvider] \u{1F50D} Discovering schema for collection "${this.indexName}"...`);
@@ -877,13 +881,16 @@ var init_QdrantProvider = __esm({
877
881
  const allFields = [.../* @__PURE__ */ new Set([...discoveredFields, ...configFields])];
878
882
  if (allFields.length > 0) {
879
883
  console.log(`[QdrantProvider] \u{1F6E0} Ensuring indexes for ${allFields.length} discovered fields...`);
880
- for (const fieldInfo of allFields) {
881
- const [fieldName, schemaType] = fieldInfo.split(":");
882
- await this.ensureIndex(fieldName, schemaType || "keyword");
883
- }
884
+ await Promise.all(
885
+ allFields.map(async (fieldInfo) => {
886
+ const [fieldName, schemaType] = fieldInfo.split(":");
887
+ return this.ensureIndex(fieldName, schemaType || "keyword");
888
+ })
889
+ );
884
890
  } else {
885
891
  console.log(`[QdrantProvider] \u2139\uFE0F No fields discovered for indexing.`);
886
892
  }
893
+ this.schemaDiscovered = true;
887
894
  }
888
895
  /**
889
896
  * Samples points from the collection to discover available payload fields.
@@ -1980,6 +1987,53 @@ ${context}`
1980
1987
  });
1981
1988
  return (_h = (_g = (_f = completion.choices[0]) == null ? void 0 : _f.message) == null ? void 0 : _g.content) != null ? _h : "";
1982
1989
  }
1990
+ chatStream(messages, context, options) {
1991
+ return __asyncGenerator(this, null, function* () {
1992
+ var _a, _b, _c, _d, _e, _f, _g;
1993
+ const systemContent = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
1994
+
1995
+ Context:
1996
+ ${context}`;
1997
+ const systemMessage = {
1998
+ role: "system",
1999
+ content: systemContent.includes("{{context}}") ? systemContent.replace("{{context}}", context) : `${systemContent}
2000
+
2001
+ Context:
2002
+ ${context}`
2003
+ };
2004
+ const formattedMessages = [
2005
+ systemMessage,
2006
+ ...messages.map((m) => ({
2007
+ role: m.role,
2008
+ content: m.content
2009
+ }))
2010
+ ];
2011
+ const stream = yield new __await(this.client.chat.completions.create({
2012
+ model: this.llmConfig.model,
2013
+ messages: formattedMessages,
2014
+ max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
2015
+ temperature: (_e = (_d = options == null ? void 0 : options.temperature) != null ? _d : this.llmConfig.temperature) != null ? _e : 0.7,
2016
+ stop: options == null ? void 0 : options.stop,
2017
+ stream: true
2018
+ }));
2019
+ try {
2020
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2021
+ const chunk = temp.value;
2022
+ const content = ((_g = (_f = chunk.choices[0]) == null ? void 0 : _f.delta) == null ? void 0 : _g.content) || "";
2023
+ if (content) yield content;
2024
+ }
2025
+ } catch (temp) {
2026
+ error = [temp];
2027
+ } finally {
2028
+ try {
2029
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
2030
+ } finally {
2031
+ if (error)
2032
+ throw error[0];
2033
+ }
2034
+ }
2035
+ });
2036
+ }
1983
2037
  async embed(text, options) {
1984
2038
  const results = await this.batchEmbed([text], options);
1985
2039
  return results[0];
@@ -2085,6 +2139,47 @@ ${context}`;
2085
2139
  const block = response.content[0];
2086
2140
  return block.type === "text" ? block.text : "";
2087
2141
  }
2142
+ chatStream(messages, context, options) {
2143
+ return __asyncGenerator(this, null, function* () {
2144
+ var _a, _b, _c;
2145
+ const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the context below to answer the user's question accurately.
2146
+
2147
+ Context:
2148
+ ${context}`;
2149
+ const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
2150
+
2151
+ Context:
2152
+ ${context}`;
2153
+ const anthropicMessages = messages.map((m) => ({
2154
+ role: m.role === "assistant" ? "assistant" : "user",
2155
+ content: m.content
2156
+ }));
2157
+ const stream = yield new __await(this.client.messages.create({
2158
+ model: this.llmConfig.model,
2159
+ max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
2160
+ system,
2161
+ messages: anthropicMessages,
2162
+ stream: true
2163
+ }));
2164
+ try {
2165
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2166
+ const chunk = temp.value;
2167
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
2168
+ yield chunk.delta.text;
2169
+ }
2170
+ }
2171
+ } catch (temp) {
2172
+ error = [temp];
2173
+ } finally {
2174
+ try {
2175
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
2176
+ } finally {
2177
+ if (error)
2178
+ throw error[0];
2179
+ }
2180
+ }
2181
+ });
2182
+ }
2088
2183
  async embed(text, options) {
2089
2184
  void text;
2090
2185
  void options;
@@ -2190,7 +2285,7 @@ var OllamaProvider = class {
2190
2285
  }
2191
2286
  chatStream(messages, context, options) {
2192
2287
  return __asyncGenerator(this, null, function* () {
2193
- var _a, _b, _c, _d, _e;
2288
+ var _a, _b, _c, _d, _e, _f;
2194
2289
  const system = this.buildSystemPrompt(context);
2195
2290
  const response = yield new __await(this.http.post("/api/chat", {
2196
2291
  model: this.llmConfig.model,
@@ -2204,11 +2299,15 @@ var OllamaProvider = class {
2204
2299
  ...messages.map((m) => ({ role: m.role, content: m.content }))
2205
2300
  ]
2206
2301
  }, { responseType: "stream" }));
2302
+ let lineBuffer = "";
2207
2303
  try {
2208
2304
  for (var iter = __forAwait(response.data), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2209
2305
  const chunk = temp.value;
2210
- const lines = chunk.toString().split("\n").filter(Boolean);
2306
+ lineBuffer += chunk.toString();
2307
+ const lines = lineBuffer.split("\n");
2308
+ lineBuffer = lines.pop() || "";
2211
2309
  for (const line of lines) {
2310
+ if (!line.trim()) continue;
2212
2311
  try {
2213
2312
  const json = JSON.parse(line);
2214
2313
  if ((_e = json.message) == null ? void 0 : _e.content) {
@@ -2216,6 +2315,7 @@ var OllamaProvider = class {
2216
2315
  }
2217
2316
  if (json.done) return;
2218
2317
  } catch (e) {
2318
+ console.warn("[OllamaProvider] Failed to parse streaming line:", line);
2219
2319
  }
2220
2320
  }
2221
2321
  }
@@ -2229,6 +2329,13 @@ var OllamaProvider = class {
2229
2329
  throw error[0];
2230
2330
  }
2231
2331
  }
2332
+ if (lineBuffer.trim()) {
2333
+ try {
2334
+ const json = JSON.parse(lineBuffer);
2335
+ if ((_f = json.message) == null ? void 0 : _f.content) yield json.message.content;
2336
+ } catch (e) {
2337
+ }
2338
+ }
2232
2339
  });
2233
2340
  }
2234
2341
  buildSystemPrompt(context) {
@@ -2374,6 +2481,49 @@ ${context}`;
2374
2481
  });
2375
2482
  return (_f = response.text) != null ? _f : "";
2376
2483
  }
2484
+ chatStream(messages, context, options) {
2485
+ return __asyncGenerator(this, null, function* () {
2486
+ var _a, _b, _c, _d, _e;
2487
+ const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
2488
+
2489
+ Context:
2490
+ ${context}`;
2491
+ const systemInstruction = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
2492
+
2493
+ Context:
2494
+ ${context}`;
2495
+ const geminiMessages = messages.map((m) => ({
2496
+ role: m.role === "assistant" ? "model" : "user",
2497
+ parts: [{ text: m.content }]
2498
+ }));
2499
+ const result = yield new __await(this.client.models.generateContentStream({
2500
+ model: this.llmConfig.model,
2501
+ contents: geminiMessages,
2502
+ config: {
2503
+ systemInstruction,
2504
+ temperature: (_c = (_b = options == null ? void 0 : options.temperature) != null ? _b : this.llmConfig.temperature) != null ? _c : 0.7,
2505
+ maxOutputTokens: (_e = (_d = options == null ? void 0 : options.maxTokens) != null ? _d : this.llmConfig.maxTokens) != null ? _e : 1024,
2506
+ stopSequences: options == null ? void 0 : options.stop
2507
+ }
2508
+ }));
2509
+ try {
2510
+ for (var iter = __forAwait(result.stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2511
+ const chunk = temp.value;
2512
+ const text = chunk.text;
2513
+ if (text) yield text;
2514
+ }
2515
+ } catch (temp) {
2516
+ error = [temp];
2517
+ } finally {
2518
+ try {
2519
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
2520
+ } finally {
2521
+ if (error)
2522
+ throw error[0];
2523
+ }
2524
+ }
2525
+ });
2526
+ }
2377
2527
  async embed(text, options) {
2378
2528
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2379
2529
  const model = this.sanitizeModel(
package/dist/server.mjs CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  sseFrame,
41
41
  sseMetaFrame,
42
42
  sseTextFrame
43
- } from "./chunk-G2LVK46T.mjs";
43
+ } from "./chunk-JZ4H7EP6.mjs";
44
44
  import "./chunk-YLTMFW4M.mjs";
45
45
  import {
46
46
  PineconeProvider
@@ -56,7 +56,7 @@ import {
56
56
  } from "./chunk-U55XRW3U.mjs";
57
57
  import {
58
58
  QdrantProvider
59
- } from "./chunk-65S4BQL2.mjs";
59
+ } from "./chunk-63HITIWC.mjs";
60
60
  import {
61
61
  BaseVectorProvider
62
62
  } from "./chunk-IMP6FUCY.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
5
  "author": "Abhinav Alkuchi",
6
6
  "license": "MIT",
@@ -98,6 +98,35 @@ export class AnthropicProvider implements ILLMProvider {
98
98
  return block.type === 'text' ? block.text : '';
99
99
  }
100
100
 
101
+ async *chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string> {
102
+ const systemPrompt =
103
+ this.llmConfig.systemPrompt ??
104
+ `You are a helpful assistant. Use the context below to answer the user's question accurately.\n\nContext:\n${context}`;
105
+
106
+ const system = systemPrompt.includes('{{context}}')
107
+ ? systemPrompt.replace('{{context}}', context)
108
+ : `${systemPrompt}\n\nContext:\n${context}`;
109
+
110
+ const anthropicMessages: Anthropic.MessageParam[] = messages.map((m) => ({
111
+ role: m.role === 'assistant' ? 'assistant' : 'user',
112
+ content: m.content,
113
+ }));
114
+
115
+ const stream = await this.client.messages.create({
116
+ model: this.llmConfig.model,
117
+ max_tokens: options?.maxTokens ?? this.llmConfig.maxTokens ?? 1024,
118
+ system,
119
+ messages: anthropicMessages,
120
+ stream: true,
121
+ });
122
+
123
+ for await (const chunk of stream) {
124
+ if (chunk.type === 'content_block_delta' && chunk.delta.type === 'text_delta') {
125
+ yield chunk.delta.text;
126
+ }
127
+ }
128
+ }
129
+
101
130
  async embed(text: string, options?: EmbedOptions): Promise<number[]> {
102
131
  void text;
103
132
  void options;
@@ -130,6 +130,37 @@ export class GeminiProvider implements ILLMProvider {
130
130
  return response.text ?? '';
131
131
  }
132
132
 
133
+ async *chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string> {
134
+ const systemPrompt =
135
+ this.llmConfig.systemPrompt ??
136
+ `You are a helpful assistant. Answer questions based on the provided context.\n\nContext:\n${context}`;
137
+
138
+ const systemInstruction = systemPrompt.includes('{{context}}')
139
+ ? systemPrompt.replace('{{context}}', context)
140
+ : `${systemPrompt}\n\nContext:\n${context}`;
141
+
142
+ const geminiMessages = messages.map((m) => ({
143
+ role: m.role === 'assistant' ? 'model' : 'user',
144
+ parts: [{ text: m.content }],
145
+ }));
146
+
147
+ const result = await this.client.models.generateContentStream({
148
+ model: this.llmConfig.model,
149
+ contents: geminiMessages,
150
+ config: {
151
+ systemInstruction,
152
+ temperature: options?.temperature ?? this.llmConfig.temperature ?? 0.7,
153
+ maxOutputTokens: options?.maxTokens ?? this.llmConfig.maxTokens ?? 1024,
154
+ stopSequences: options?.stop,
155
+ },
156
+ });
157
+
158
+ for await (const chunk of result.stream) {
159
+ const text = chunk.text;
160
+ if (text) yield text;
161
+ }
162
+ }
163
+
133
164
  async embed(text: string, options?: EmbedOptions): Promise<number[]> {
134
165
  const model = this.sanitizeModel(
135
166
  options?.model ??
@@ -118,9 +118,16 @@ export class OllamaProvider implements ILLMProvider {
118
118
  ],
119
119
  }, { responseType: 'stream' });
120
120
 
121
+ let lineBuffer = '';
121
122
  for await (const chunk of response.data) {
122
- const lines = chunk.toString().split('\n').filter(Boolean);
123
+ lineBuffer += chunk.toString();
124
+ const lines = lineBuffer.split('\n');
125
+
126
+ // Keep the last partial line in the buffer
127
+ lineBuffer = lines.pop() || '';
128
+
123
129
  for (const line of lines) {
130
+ if (!line.trim()) continue;
124
131
  try {
125
132
  const json = JSON.parse(line);
126
133
  if (json.message?.content) {
@@ -128,10 +135,22 @@ export class OllamaProvider implements ILLMProvider {
128
135
  }
129
136
  if (json.done) return;
130
137
  } catch {
131
- // Ignore parsing errors for partial lines
138
+ // If parsing fails, it might be a malformed line or something else
139
+ // But with line buffering, this should be rare
140
+ console.warn('[OllamaProvider] Failed to parse streaming line:', line);
132
141
  }
133
142
  }
134
143
  }
144
+
145
+ // Process anything left in the buffer at the end
146
+ if (lineBuffer.trim()) {
147
+ try {
148
+ const json = JSON.parse(lineBuffer);
149
+ if (json.message?.content) yield json.message.content;
150
+ } catch {
151
+ // Final cleanup
152
+ }
153
+ }
135
154
  }
136
155
 
137
156
  private buildSystemPrompt(context: string): string {
@@ -108,6 +108,41 @@ export class OpenAIProvider implements ILLMProvider {
108
108
 
109
109
  return completion.choices[0]?.message?.content ?? '';
110
110
  }
111
+
112
+ async *chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string> {
113
+ const systemContent =
114
+ this.llmConfig.systemPrompt ??
115
+ `You are a helpful assistant. Answer questions based on the provided context.\n\nContext:\n${context}`;
116
+
117
+ const systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = {
118
+ role: 'system',
119
+ content: systemContent.includes('{{context}}')
120
+ ? systemContent.replace('{{context}}', context)
121
+ : `${systemContent}\n\nContext:\n${context}`,
122
+ };
123
+
124
+ const formattedMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
125
+ systemMessage,
126
+ ...messages.map((m) => ({
127
+ role: m.role as 'user' | 'assistant',
128
+ content: m.content,
129
+ })),
130
+ ];
131
+
132
+ const stream = await this.client.chat.completions.create({
133
+ model: this.llmConfig.model,
134
+ messages: formattedMessages,
135
+ max_tokens: options?.maxTokens ?? this.llmConfig.maxTokens ?? 1024,
136
+ temperature: options?.temperature ?? this.llmConfig.temperature ?? 0.7,
137
+ stop: options?.stop,
138
+ stream: true,
139
+ });
140
+
141
+ for await (const chunk of stream) {
142
+ const content = chunk.choices[0]?.delta?.content || '';
143
+ if (content) yield content;
144
+ }
145
+ }
111
146
 
112
147
  async embed(text: string, options?: EmbedOptions): Promise<number[]> {
113
148
  const results = await this.batchEmbed([text], options);
@@ -13,6 +13,7 @@ export class QdrantProvider extends BaseVectorProvider {
13
13
  private contentField: string;
14
14
  private metadataField: string;
15
15
  private isFlatPayload: boolean;
16
+ private schemaDiscovered = false;
16
17
 
17
18
  constructor(config: VectorDBConfig) {
18
19
  super(config);
@@ -26,6 +27,7 @@ export class QdrantProvider extends BaseVectorProvider {
26
27
 
27
28
  this.http = axios.create({
28
29
  baseURL: baseUrl,
30
+ timeout: 15000, // 15s timeout for vector DB operations
29
31
  headers: {
30
32
  'Content-Type': 'application/json',
31
33
  ...(opts.apiKey ? { 'api-key': opts.apiKey as string } : {}),
@@ -34,6 +36,8 @@ export class QdrantProvider extends BaseVectorProvider {
34
36
  }
35
37
 
36
38
  async initialize(): Promise<void> {
39
+ if (this.schemaDiscovered) return;
40
+
37
41
  await this.ping();
38
42
  await this.ensureCollection();
39
43
 
@@ -49,13 +53,18 @@ export class QdrantProvider extends BaseVectorProvider {
49
53
 
50
54
  if (allFields.length > 0) {
51
55
  console.log(`[QdrantProvider] 🛠 Ensuring indexes for ${allFields.length} discovered fields...`);
52
- for (const fieldInfo of allFields) {
53
- const [fieldName, schemaType] = fieldInfo.split(':');
54
- await this.ensureIndex(fieldName, (schemaType as any) || 'keyword');
55
- }
56
+ // Parallelize index creation for much faster startup
57
+ await Promise.all(
58
+ allFields.map(async (fieldInfo) => {
59
+ const [fieldName, schemaType] = fieldInfo.split(':');
60
+ return this.ensureIndex(fieldName, (schemaType as any) || 'keyword');
61
+ })
62
+ );
56
63
  } else {
57
64
  console.log(`[QdrantProvider] ℹ️ No fields discovered for indexing.`);
58
65
  }
66
+
67
+ this.schemaDiscovered = true;
59
68
  }
60
69
 
61
70
  /**