@retrivora-ai/rag-engine 1.0.0 → 1.0.2

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 (44) hide show
  1. package/dist/{ChromaDBProvider-APQVJ5F7.mjs → ChromaDBProvider-MIDOR4FW.mjs} +2 -2
  2. package/dist/{MilvusProvider-35US67MS.mjs → MilvusProvider-U7SKC27V.mjs} +2 -2
  3. package/dist/{MongoDBProvider-COVYZDP6.mjs → MongoDBProvider-BO2Y5DRR.mjs} +2 -2
  4. package/dist/{PineconeProvider-AWFJQDZL.mjs → PineconeProvider-QZNRKTN2.mjs} +2 -2
  5. package/dist/{PostgreSQLProvider-IEYRJ7XJ.mjs → PostgreSQLProvider-BMOETDZA.mjs} +1 -1
  6. package/dist/{QdrantProvider-M6TQYZRO.mjs → QdrantProvider-YNUNEOZH.mjs} +2 -2
  7. package/dist/{RedisProvider-3G5PBLZ4.mjs → RedisProvider-SR65SCKV.mjs} +2 -2
  8. package/dist/{SimpleGraphProvider-UK7DJW37.mjs → SimpleGraphProvider-SLOXO4M7.mjs} +1 -1
  9. package/dist/{UniversalVectorProvider-FYQ3B2PW.mjs → UniversalVectorProvider-IN67OS56.mjs} +3 -3
  10. package/dist/{WeaviateProvider-ITHO36IL.mjs → WeaviateProvider-5FWDFITI.mjs} +2 -2
  11. package/dist/{chunk-4A47RCG2.mjs → chunk-3DSHW676.mjs} +1 -1
  12. package/dist/{chunk-NXUCKY5L.mjs → chunk-5YGUXK7Z.mjs} +1 -1
  13. package/dist/{chunk-7SOSCZGS.mjs → chunk-73I6VWU3.mjs} +2 -2
  14. package/dist/{chunk-WGSZNY3X.mjs → chunk-CFVEZTBJ.mjs} +1 -1
  15. package/dist/{chunk-U6KHVZLF.mjs → chunk-LR3VMDVK.mjs} +2 -2
  16. package/dist/{chunk-OOQXNLXD.mjs → chunk-M6JSPGAR.mjs} +1 -1
  17. package/dist/{chunk-P4HAQ7KB.mjs → chunk-OJNAKSQ2.mjs} +90 -34
  18. package/dist/{chunk-TYHTZIDP.mjs → chunk-U55XRW3U.mjs} +1 -1
  19. package/dist/{chunk-ZNBKHNJ4.mjs → chunk-VUQJVIJT.mjs} +1 -1
  20. package/dist/{chunk-QMIKLALV.mjs → chunk-X4TOT24V.mjs} +32 -0
  21. package/dist/{chunk-67AJ6SMD.mjs → chunk-YLTMFW4M.mjs} +1 -1
  22. package/dist/handlers/index.d.mts +1 -1
  23. package/dist/handlers/index.d.ts +1 -1
  24. package/dist/handlers/index.js +109 -22
  25. package/dist/handlers/index.mjs +5 -3
  26. package/dist/{index-CrGMwXfO.d.ts → index-B2mutkgp.d.ts} +5 -1
  27. package/dist/{index-v669iV-k.d.mts → index-Bjy0es5a.d.mts} +5 -1
  28. package/dist/index.d.mts +13 -7
  29. package/dist/index.d.ts +13 -7
  30. package/dist/index.js +325 -380
  31. package/dist/index.mjs +315 -370
  32. package/dist/server.d.mts +7 -22
  33. package/dist/server.d.ts +7 -22
  34. package/dist/server.js +48 -22
  35. package/dist/server.mjs +11 -11
  36. package/package.json +1 -1
  37. package/src/components/ChatWindow.tsx +4 -3
  38. package/src/components/MessageBubble.tsx +5 -2
  39. package/src/core/Pipeline.ts +1 -0
  40. package/src/core/VectorPlugin.ts +13 -22
  41. package/src/handlers/index.ts +53 -0
  42. package/src/hooks/useRagChat.ts +79 -15
  43. package/src/providers/vectordb/MongoDBProvider.ts +1 -1
  44. package/src/rag/EntityExtractor.ts +3 -0
package/dist/server.d.mts CHANGED
@@ -2,8 +2,8 @@ import { e as VectorDBConfig, L as LLMConfig, E as EmbeddingConfig, c as RagConf
2
2
  export { R as RAGConfig, U as UIConfig } from './RagConfig-DRJO4hGU.mjs';
3
3
  import { C as ChatMessage, I as ILLMProvider, a as ChatOptions, E as EmbedOptions } from './DocumentChunker-C-sCZPhi.mjs';
4
4
  export { b as Chunk, c as ChunkOptions, D as DocumentChunker } from './DocumentChunker-C-sCZPhi.mjs';
5
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-v669iV-k.mjs';
6
- export { C as ConfigValidator, V as ValidationError, c as createChatHandler, b as createHealthHandler, d as createIngestHandler, e as createUploadHandler } from './index-v669iV-k.mjs';
5
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-Bjy0es5a.mjs';
6
+ export { C as ConfigValidator, V as ValidationError, c as createChatHandler, b as createHealthHandler, d as createIngestHandler, e as createUploadHandler } from './index-Bjy0es5a.mjs';
7
7
  import 'next/server';
8
8
 
9
9
  /**
@@ -111,26 +111,7 @@ declare class ProviderHealthCheck {
111
111
  declare class VectorPlugin {
112
112
  private config;
113
113
  private pipeline;
114
- /**
115
- * Initializes the plugin with the host configuration.
116
- * @param hostConfig - Configuration object passed from the host application.
117
- * @throws Error if configuration is invalid or providers are unhealthy
118
- *
119
- * @example
120
- * const plugin = new VectorPlugin({
121
- * projectId: 'my-app',
122
- * vectorDb: {
123
- * provider: 'pinecone',
124
- * indexName: 'my-index',
125
- * options: { apiKey: process.env.PINECONE_API_KEY }
126
- * },
127
- * llm: {
128
- * provider: 'openai',
129
- * model: 'gpt-4o',
130
- * apiKey: process.env.OPENAI_API_KEY
131
- * }
132
- * });
133
- */
114
+ private validationPromise;
134
115
  constructor(hostConfig?: Partial<RagConfig>);
135
116
  /**
136
117
  * Get the current resolved configuration.
@@ -152,6 +133,10 @@ declare class VectorPlugin {
152
133
  * Run a chat query.
153
134
  */
154
135
  chat(message: string, history?: ChatMessage[], namespace?: string): Promise<ChatResponse>;
136
+ /**
137
+ * Run a streaming chat query.
138
+ */
139
+ chatStream(message: string, history?: ChatMessage[], namespace?: string): AsyncGenerator<string | ChatResponse, void, any>;
155
140
  /**
156
141
  * Ingest documents into the vector database.
157
142
  */
package/dist/server.d.ts CHANGED
@@ -2,8 +2,8 @@ import { e as VectorDBConfig, L as LLMConfig, E as EmbeddingConfig, c as RagConf
2
2
  export { R as RAGConfig, U as UIConfig } from './RagConfig-DRJO4hGU.js';
3
3
  import { C as ChatMessage, I as ILLMProvider, a as ChatOptions, E as EmbedOptions } from './DocumentChunker-C-sCZPhi.js';
4
4
  export { b as Chunk, c as ChunkOptions, D as DocumentChunker } from './DocumentChunker-C-sCZPhi.js';
5
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-CrGMwXfO.js';
6
- export { C as ConfigValidator, V as ValidationError, c as createChatHandler, b as createHealthHandler, d as createIngestHandler, e as createUploadHandler } from './index-CrGMwXfO.js';
5
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-B2mutkgp.js';
6
+ export { C as ConfigValidator, V as ValidationError, c as createChatHandler, b as createHealthHandler, d as createIngestHandler, e as createUploadHandler } from './index-B2mutkgp.js';
7
7
  import 'next/server';
8
8
 
9
9
  /**
@@ -111,26 +111,7 @@ declare class ProviderHealthCheck {
111
111
  declare class VectorPlugin {
112
112
  private config;
113
113
  private pipeline;
114
- /**
115
- * Initializes the plugin with the host configuration.
116
- * @param hostConfig - Configuration object passed from the host application.
117
- * @throws Error if configuration is invalid or providers are unhealthy
118
- *
119
- * @example
120
- * const plugin = new VectorPlugin({
121
- * projectId: 'my-app',
122
- * vectorDb: {
123
- * provider: 'pinecone',
124
- * indexName: 'my-index',
125
- * options: { apiKey: process.env.PINECONE_API_KEY }
126
- * },
127
- * llm: {
128
- * provider: 'openai',
129
- * model: 'gpt-4o',
130
- * apiKey: process.env.OPENAI_API_KEY
131
- * }
132
- * });
133
- */
114
+ private validationPromise;
134
115
  constructor(hostConfig?: Partial<RagConfig>);
135
116
  /**
136
117
  * Get the current resolved configuration.
@@ -152,6 +133,10 @@ declare class VectorPlugin {
152
133
  * Run a chat query.
153
134
  */
154
135
  chat(message: string, history?: ChatMessage[], namespace?: string): Promise<ChatResponse>;
136
+ /**
137
+ * Run a streaming chat query.
138
+ */
139
+ chatStream(message: string, history?: ChatMessage[], namespace?: string): AsyncGenerator<string | ChatResponse, void, any>;
155
140
  /**
156
141
  * Ingest documents into the vector database.
157
142
  */
package/dist/server.js CHANGED
@@ -10,6 +10,9 @@ var __getProtoOf = Object.getPrototypeOf;
10
10
  var __hasOwnProp = Object.prototype.hasOwnProperty;
11
11
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
12
12
  var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
13
+ var __typeError = (msg) => {
14
+ throw TypeError(msg);
15
+ };
13
16
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
14
17
  var __spreadValues = (a, b) => {
15
18
  for (var prop in b || (b = {}))
@@ -74,6 +77,34 @@ var __asyncGenerator = (__this, __arguments, generator) => {
74
77
  }, method = (k, call, wait, clear) => it[k] = (x) => (call = new Promise((yes, no, run) => (run = () => resume(k, x, yes, no), q ? q.then(run) : run())), clear = () => q === wait && (q = 0), q = wait = call.then(clear, clear), call), q, it = {};
75
78
  return generator = generator.apply(__this, __arguments), it[__knownSymbol("asyncIterator")] = () => it, method("next"), method("throw"), method("return"), it;
76
79
  };
80
+ var __yieldStar = (value) => {
81
+ var obj = value[__knownSymbol("asyncIterator")], isAwait = false, method, it = {};
82
+ if (obj == null) {
83
+ obj = value[__knownSymbol("iterator")]();
84
+ method = (k) => it[k] = (x) => obj[k](x);
85
+ } else {
86
+ obj = obj.call(value);
87
+ method = (k) => it[k] = (v) => {
88
+ if (isAwait) {
89
+ isAwait = false;
90
+ if (k === "throw") throw v;
91
+ return v;
92
+ }
93
+ isAwait = true;
94
+ return {
95
+ done: false,
96
+ value: new __await(new Promise((resolve) => {
97
+ var x = obj[k](v);
98
+ if (!(x instanceof Object)) __typeError("Object expected");
99
+ resolve(x);
100
+ }), 1)
101
+ };
102
+ };
103
+ }
104
+ return it[__knownSymbol("iterator")] = () => it, method("next"), "throw" in obj ? method("throw") : it.throw = (x) => {
105
+ throw x;
106
+ }, "return" in obj && method("return"), it;
107
+ };
77
108
  var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
78
109
 
79
110
  // src/utils/templateUtils.ts
@@ -527,7 +558,7 @@ var init_MongoDBProvider = __esm({
527
558
  if (!opts.uri || !opts.database || !opts.collection) {
528
559
  throw new Error("[MongoDBProvider] options uri, database, and collection are required.");
529
560
  }
530
- this.client = new import_mongodb.MongoClient(opts.uri);
561
+ this.client = new import_mongodb.MongoClient(opts.uri, { serverSelectionTimeoutMS: 5e3 });
531
562
  this.dbName = opts.database;
532
563
  this.collectionName = opts.collection;
533
564
  this.embeddingKey = opts.embeddingKey || "embedding";
@@ -2908,6 +2939,9 @@ Extract entities and relationships from the following text.
2908
2939
  Format the output as JSON with two keys: "nodes" (array of {id, label, properties}) and "edges" (array of {source, target, type, properties}).
2909
2940
  Use the same ID for the same entity.
2910
2941
 
2942
+ IMPORTANT: Ensure all property values are valid JSON types (strings, numbers, booleans, or null).
2943
+ DO NOT include mathematical expressions like "4.5/5" as numbers; use strings instead.
2944
+
2911
2945
  Text:
2912
2946
  "${text}"
2913
2947
 
@@ -3488,6 +3522,7 @@ var Pipeline = class {
3488
3522
  async initialize() {
3489
3523
  var _a;
3490
3524
  if (this.initialised) return;
3525
+ console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
3491
3526
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
3492
3527
  const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
3493
3528
  this.config.llm,
@@ -3872,29 +3907,9 @@ var ProviderHealthCheck = class {
3872
3907
 
3873
3908
  // src/core/VectorPlugin.ts
3874
3909
  var VectorPlugin = class {
3875
- /**
3876
- * Initializes the plugin with the host configuration.
3877
- * @param hostConfig - Configuration object passed from the host application.
3878
- * @throws Error if configuration is invalid or providers are unhealthy
3879
- *
3880
- * @example
3881
- * const plugin = new VectorPlugin({
3882
- * projectId: 'my-app',
3883
- * vectorDb: {
3884
- * provider: 'pinecone',
3885
- * indexName: 'my-index',
3886
- * options: { apiKey: process.env.PINECONE_API_KEY }
3887
- * },
3888
- * llm: {
3889
- * provider: 'openai',
3890
- * model: 'gpt-4o',
3891
- * apiKey: process.env.OPENAI_API_KEY
3892
- * }
3893
- * });
3894
- */
3895
3910
  constructor(hostConfig) {
3896
3911
  this.config = ConfigResolver.resolve(hostConfig);
3897
- ConfigValidator.validateAndThrow(this.config);
3912
+ this.validationPromise = ConfigValidator.validateAndThrow(this.config);
3898
3913
  this.pipeline = new Pipeline(this.config);
3899
3914
  }
3900
3915
  /**
@@ -3920,12 +3935,23 @@ var VectorPlugin = class {
3920
3935
  * Run a chat query.
3921
3936
  */
3922
3937
  async chat(message, history = [], namespace) {
3938
+ await this.validationPromise;
3923
3939
  return this.pipeline.ask(message, history, namespace);
3924
3940
  }
3941
+ /**
3942
+ * Run a streaming chat query.
3943
+ */
3944
+ chatStream(_0) {
3945
+ return __asyncGenerator(this, arguments, function* (message, history = [], namespace) {
3946
+ yield new __await(this.validationPromise);
3947
+ yield* __yieldStar(this.pipeline.askStream(message, history, namespace));
3948
+ });
3949
+ }
3925
3950
  /**
3926
3951
  * Ingest documents into the vector database.
3927
3952
  */
3928
3953
  async ingest(documents, namespace) {
3954
+ await this.validationPromise;
3929
3955
  return this.pipeline.ingest(documents, namespace);
3930
3956
  }
3931
3957
  };
package/dist/server.mjs CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  ChromaDBProvider
3
- } from "./chunk-WGSZNY3X.mjs";
3
+ } from "./chunk-CFVEZTBJ.mjs";
4
4
  import {
5
5
  RedisProvider
6
- } from "./chunk-NXUCKY5L.mjs";
6
+ } from "./chunk-5YGUXK7Z.mjs";
7
7
  import {
8
8
  WeaviateProvider
9
- } from "./chunk-OOQXNLXD.mjs";
9
+ } from "./chunk-M6JSPGAR.mjs";
10
10
  import {
11
11
  UniversalVectorProvider
12
- } from "./chunk-U6KHVZLF.mjs";
12
+ } from "./chunk-LR3VMDVK.mjs";
13
13
  import {
14
14
  AnthropicProvider,
15
15
  BatchProcessor,
@@ -34,23 +34,23 @@ import {
34
34
  createIngestHandler,
35
35
  createUploadHandler,
36
36
  getRagConfig
37
- } from "./chunk-P4HAQ7KB.mjs";
38
- import "./chunk-67AJ6SMD.mjs";
37
+ } from "./chunk-OJNAKSQ2.mjs";
38
+ import "./chunk-YLTMFW4M.mjs";
39
39
  import {
40
40
  PineconeProvider
41
- } from "./chunk-ZNBKHNJ4.mjs";
41
+ } from "./chunk-VUQJVIJT.mjs";
42
42
  import {
43
43
  PostgreSQLProvider
44
44
  } from "./chunk-FLOSGE6A.mjs";
45
45
  import {
46
46
  MongoDBProvider
47
- } from "./chunk-7SOSCZGS.mjs";
47
+ } from "./chunk-73I6VWU3.mjs";
48
48
  import {
49
49
  MilvusProvider
50
- } from "./chunk-TYHTZIDP.mjs";
50
+ } from "./chunk-U55XRW3U.mjs";
51
51
  import {
52
52
  QdrantProvider
53
- } from "./chunk-4A47RCG2.mjs";
53
+ } from "./chunk-3DSHW676.mjs";
54
54
  import {
55
55
  BaseVectorProvider
56
56
  } from "./chunk-IMP6FUCY.mjs";
@@ -58,7 +58,7 @@ import {
58
58
  __objRest,
59
59
  __spreadProps,
60
60
  __spreadValues
61
- } from "./chunk-QMIKLALV.mjs";
61
+ } from "./chunk-X4TOT24V.mjs";
62
62
 
63
63
  // src/config/ConfigBuilder.ts
64
64
  var ConfigBuilder = class {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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",
@@ -170,19 +170,20 @@ export function ChatWindow({ className = '', style, onClose, showClose = false }
170
170
  </div>
171
171
  </div>
172
172
  ) : (
173
- messages.map((msg) => (
173
+ messages.map((msg, index) => (
174
174
  <MessageBubble
175
175
  key={msg.id}
176
176
  message={msg}
177
177
  sources={msg.sources}
178
+ isStreaming={isLoading && index === messages.length - 1 && msg.role === 'assistant'}
178
179
  primaryColor={ui.primaryColor}
179
180
  accentColor={ui.accentColor}
180
181
  />
181
182
  ))
182
183
  )}
183
184
 
184
- {/* Typing indicator */}
185
- {isLoading && (
185
+ {/* Typing indicator (only if assistant hasn't started replying yet) */}
186
+ {isLoading && messages[messages.length - 1]?.role !== 'assistant' && (
186
187
  <MessageBubble
187
188
  message={{ role: 'assistant', content: '' }}
188
189
  isStreaming
@@ -55,8 +55,8 @@ export function MessageBubble({
55
55
  : {}
56
56
  }
57
57
  >
58
- {isStreaming ? (
59
- <span className="flex items-center gap-1">
58
+ {isStreaming && !message.content ? (
59
+ <span className="flex items-center gap-1 py-1">
60
60
  <span className="w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" />
61
61
  <span className="w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" />
62
62
  <span className="w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" />
@@ -66,6 +66,9 @@ export function MessageBubble({
66
66
  <ReactMarkdown remarkPlugins={[remarkGfm]}>
67
67
  {message.content}
68
68
  </ReactMarkdown>
69
+ {isStreaming && message.content && (
70
+ <span className="inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" />
71
+ )}
69
72
  </div>
70
73
  )}
71
74
  </div>
@@ -50,6 +50,7 @@ export class Pipeline {
50
50
  async initialize(): Promise<void> {
51
51
  if (this.initialised) return;
52
52
 
53
+ console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
53
54
  // Resolve vector DB provider
54
55
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
55
56
 
@@ -20,32 +20,13 @@ import { ChatMessage } from '../llm/ILLMProvider';
20
20
  export class VectorPlugin {
21
21
  private config: RagConfig;
22
22
  private pipeline: Pipeline;
23
+ private validationPromise: Promise<void>;
23
24
 
24
- /**
25
- * Initializes the plugin with the host configuration.
26
- * @param hostConfig - Configuration object passed from the host application.
27
- * @throws Error if configuration is invalid or providers are unhealthy
28
- *
29
- * @example
30
- * const plugin = new VectorPlugin({
31
- * projectId: 'my-app',
32
- * vectorDb: {
33
- * provider: 'pinecone',
34
- * indexName: 'my-index',
35
- * options: { apiKey: process.env.PINECONE_API_KEY }
36
- * },
37
- * llm: {
38
- * provider: 'openai',
39
- * model: 'gpt-4o',
40
- * apiKey: process.env.OPENAI_API_KEY
41
- * }
42
- * });
43
- */
44
25
  constructor(hostConfig?: Partial<RagConfig>) {
45
26
  this.config = ConfigResolver.resolve(hostConfig);
46
27
 
47
- // Validate configuration early with detailed error messages
48
- ConfigValidator.validateAndThrow(this.config);
28
+ // Start validation early
29
+ this.validationPromise = ConfigValidator.validateAndThrow(this.config);
49
30
 
50
31
  this.pipeline = new Pipeline(this.config);
51
32
  }
@@ -80,13 +61,23 @@ export class VectorPlugin {
80
61
  * Run a chat query.
81
62
  */
82
63
  async chat(message: string, history: ChatMessage[] = [], namespace?: string): Promise<ChatResponse> {
64
+ await this.validationPromise;
83
65
  return this.pipeline.ask(message, history, namespace);
84
66
  }
85
67
 
68
+ /**
69
+ * Run a streaming chat query.
70
+ */
71
+ async *chatStream(message: string, history: ChatMessage[] = [], namespace?: string) {
72
+ await this.validationPromise;
73
+ yield* this.pipeline.askStream(message, history, namespace);
74
+ }
75
+
86
76
  /**
87
77
  * Ingest documents into the vector database.
88
78
  */
89
79
  async ingest(documents: IngestDocument[], namespace?: string): Promise<Array<{ docId: string | number; chunksIngested: number }>> {
80
+ await this.validationPromise;
90
81
  return this.pipeline.ingest(documents, namespace);
91
82
  }
92
83
 
@@ -32,6 +32,59 @@ export function createChatHandler(config?: Partial<RagConfig>) {
32
32
  };
33
33
  }
34
34
 
35
+ /**
36
+ * createStreamHandler — factory that returns a streaming Next.js App Router POST handler
37
+ */
38
+ export function createStreamHandler(config?: Partial<RagConfig>) {
39
+ const plugin = new VectorPlugin(config);
40
+
41
+ return async function POST(req: NextRequest) {
42
+ try {
43
+ const body = await req.json();
44
+ const { message, history = [], namespace } = body;
45
+
46
+ const encoder = new TextEncoder();
47
+ const stream = new ReadableStream({
48
+ async start(controller) {
49
+ try {
50
+ const pipelineStream = plugin.chatStream(message, history, namespace);
51
+
52
+ for await (const chunk of pipelineStream) {
53
+ if (typeof chunk === 'string') {
54
+ controller.enqueue(encoder.encode(chunk));
55
+ } else {
56
+ // Yield metadata/sources at the end
57
+ controller.enqueue(encoder.encode(`\n\n__METADATA__${JSON.stringify(chunk)}`));
58
+ }
59
+ }
60
+ controller.close();
61
+ } catch (streamError) {
62
+ console.error('[createStreamHandler] Stream processing error:', streamError);
63
+ const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
64
+ controller.enqueue(encoder.encode(`\n\n__ERROR__${JSON.stringify({ error: errorMessage })}`));
65
+ controller.close();
66
+ }
67
+ },
68
+ });
69
+
70
+ return new Response(stream, {
71
+ headers: {
72
+ 'Content-Type': 'text/event-stream',
73
+ 'Cache-Control': 'no-cache',
74
+ 'Connection': 'keep-alive',
75
+ },
76
+ });
77
+ } catch (err) {
78
+ const message = err instanceof Error ? err.message : 'Internal server error';
79
+ return new Response(JSON.stringify({ error: message }), {
80
+ status: 500,
81
+ headers: { 'Content-Type': 'application/json' },
82
+ });
83
+ }
84
+ };
85
+ }
86
+
87
+
35
88
  /**
36
89
  * createIngestHandler — factory for the document ingestion endpoint.
37
90
  */
@@ -14,7 +14,6 @@
14
14
  */
15
15
 
16
16
  import { useState, useCallback, useRef, useEffect } from 'react';
17
- import axios from 'axios';
18
17
  import { VectorMatch } from '@/types';
19
18
  import { useStoredMessages } from './useStoredMessages';
20
19
 
@@ -111,26 +110,91 @@ export function useRagChat(
111
110
  try {
112
111
  const history = messagesRef.current.map(({ role, content }) => ({ role, content }));
113
112
 
114
- const { data } = await axios.post(apiUrl, {
115
- message: trimmed,
116
- history,
117
- namespace: namespace ?? projectId,
113
+ const response = await fetch(apiUrl, {
114
+ method: 'POST',
115
+ headers: { 'Content-Type': 'application/json' },
116
+ body: JSON.stringify({
117
+ message: trimmed,
118
+ history,
119
+ namespace: namespace ?? projectId,
120
+ }),
118
121
  });
119
122
 
120
- const assistantMessage: RagMessage = {
121
- id: `assistant_${Date.now()}`,
123
+ if (!response.ok) {
124
+ const errorData = await response.json().catch(() => ({}));
125
+ throw new Error(errorData.error || `Server returned ${response.status}`);
126
+ }
127
+
128
+ if (!response.body) {
129
+ throw new Error('No response body received from server');
130
+ }
131
+
132
+ const reader = response.body.getReader();
133
+ const decoder = new TextDecoder();
134
+ let assistantContent = '';
135
+ let sources: VectorMatch[] = [];
136
+
137
+ // Add a placeholder assistant message that we will update
138
+ const assistantMessageId = `assistant_${Date.now()}`;
139
+ setMessages((prev) => [
140
+ ...prev,
141
+ {
142
+ id: assistantMessageId,
143
+ role: 'assistant',
144
+ content: '',
145
+ createdAt: new Date().toISOString(),
146
+ },
147
+ ]);
148
+
149
+ while (true) {
150
+ const { done, value } = await reader.read();
151
+ if (done) break;
152
+
153
+ const chunk = decoder.decode(value, { stream: true });
154
+
155
+ // Handle protocol tokens
156
+ if (chunk.includes('__METADATA__')) {
157
+ const [text, metadataStr] = chunk.split('__METADATA__');
158
+ if (text) assistantContent += text;
159
+ try {
160
+ const meta = JSON.parse(metadataStr);
161
+ sources = meta.sources || [];
162
+ } catch {
163
+ console.warn('[useRagChat] Failed to parse metadata');
164
+ }
165
+ } else if (chunk.includes('__ERROR__')) {
166
+ const [text, errorStr] = chunk.split('__ERROR__');
167
+ if (text) assistantContent += text;
168
+ try {
169
+ const err = JSON.parse(errorStr);
170
+ setError(err.error || 'Stream error');
171
+ } catch {
172
+ setError('An error occurred during streaming');
173
+ }
174
+ } else {
175
+ assistantContent += chunk;
176
+ }
177
+
178
+ // Update the specific message
179
+ setMessages((prev) =>
180
+ prev.map((msg) =>
181
+ msg.id === assistantMessageId
182
+ ? { ...msg, content: assistantContent, sources: sources.length > 0 ? sources : msg.sources }
183
+ : msg
184
+ )
185
+ );
186
+ }
187
+
188
+ const finalAssistantMessage: RagMessage = {
189
+ id: assistantMessageId,
122
190
  role: 'assistant',
123
- content: data.reply,
124
- sources: data.sources,
191
+ content: assistantContent,
192
+ sources: sources.length > 0 ? sources : undefined,
125
193
  createdAt: new Date().toISOString(),
126
194
  };
127
-
128
- setMessages((prev) => [...prev, assistantMessage]);
129
- onReply?.(assistantMessage);
195
+ onReply?.(finalAssistantMessage);
130
196
  } catch (err) {
131
- const msg = axios.isAxiosError(err)
132
- ? (err.response?.data?.error ?? err.message)
133
- : 'Something went wrong. Please try again.';
197
+ const msg = err instanceof Error ? err.message : 'Something went wrong. Please try again.';
134
198
  setError(msg);
135
199
  onError?.(msg);
136
200
  } finally {
@@ -25,7 +25,7 @@ export class MongoDBProvider extends BaseVectorProvider {
25
25
  if (!opts.uri || !opts.database || !opts.collection) {
26
26
  throw new Error('[MongoDBProvider] options uri, database, and collection are required.');
27
27
  }
28
- this.client = new MongoClient(opts.uri as string);
28
+ this.client = new MongoClient(opts.uri as string, { serverSelectionTimeoutMS: 5000 });
29
29
  this.dbName = opts.database as string;
30
30
  this.collectionName = opts.collection as string;
31
31
  this.embeddingKey = (opts.embeddingKey as string) || 'embedding';
@@ -20,6 +20,9 @@ Extract entities and relationships from the following text.
20
20
  Format the output as JSON with two keys: "nodes" (array of {id, label, properties}) and "edges" (array of {source, target, type, properties}).
21
21
  Use the same ID for the same entity.
22
22
 
23
+ IMPORTANT: Ensure all property values are valid JSON types (strings, numbers, booleans, or null).
24
+ DO NOT include mathematical expressions like "4.5/5" as numbers; use strings instead.
25
+
23
26
  Text:
24
27
  "${text}"
25
28