@retrivora-ai/rag-engine 1.0.4 → 1.0.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 (39) hide show
  1. package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
  2. package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
  3. package/dist/{RagConfig-DRJO4hGU.d.mts → RagConfig-BEmz4hVP.d.mts} +58 -1
  4. package/dist/{RagConfig-DRJO4hGU.d.ts → RagConfig-BEmz4hVP.d.ts} +58 -1
  5. package/dist/{chunk-6MLZHQZT.mjs → chunk-MWL4AGQI.mjs} +189 -98
  6. package/dist/handlers/index.d.mts +2 -2
  7. package/dist/handlers/index.d.ts +2 -2
  8. package/dist/handlers/index.js +191 -100
  9. package/dist/handlers/index.mjs +11 -3
  10. package/dist/index-CSfaCeux.d.ts +206 -0
  11. package/dist/index-DFSy0CG6.d.mts +206 -0
  12. package/dist/index.d.mts +3 -4
  13. package/dist/index.d.ts +3 -4
  14. package/dist/index.js +103 -89
  15. package/dist/index.mjs +91 -95
  16. package/dist/server.d.mts +45 -97
  17. package/dist/server.d.ts +45 -97
  18. package/dist/server.js +271 -215
  19. package/dist/server.mjs +78 -167
  20. package/package.json +12 -10
  21. package/src/components/ChatWindow.tsx +2 -2
  22. package/src/components/ConfigProvider.tsx +2 -2
  23. package/src/components/MessageBubble.tsx +2 -2
  24. package/src/components/SourceCard.tsx +1 -1
  25. package/src/components/ThemeToggle.tsx +1 -1
  26. package/src/config/ConfigBuilder.ts +86 -211
  27. package/src/config/uiConstants.ts +23 -0
  28. package/src/core/ConfigResolver.ts +57 -29
  29. package/src/core/LangChainAgent.ts +73 -40
  30. package/src/core/Pipeline.ts +63 -7
  31. package/src/core/ProviderRegistry.ts +2 -2
  32. package/src/core/QueryProcessor.ts +9 -8
  33. package/src/handlers/index.ts +138 -49
  34. package/src/hooks/useRagChat.ts +71 -32
  35. package/src/server.ts +12 -2
  36. package/dist/DocumentChunker-C-sCZPhi.d.mts +0 -102
  37. package/dist/DocumentChunker-C-sCZPhi.d.ts +0 -102
  38. package/dist/index-B2mutkgp.d.ts +0 -116
  39. package/dist/index-Bjy0es5a.d.mts +0 -116
@@ -0,0 +1,45 @@
1
+ /**
2
+ * DocumentChunker — splits long text into overlapping chunks
3
+ * suitable for vector embedding and retrieval.
4
+ *
5
+ * Strategy: sentence-aware sliding window
6
+ * 1. Split on sentence boundaries
7
+ * 2. Accumulate sentences into chunks up to `chunkSize` characters
8
+ * 3. Carry over `chunkOverlap` characters from the previous chunk
9
+ */
10
+ interface Chunk {
11
+ id: string;
12
+ content: string;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ interface ChunkOptions {
16
+ /** Target chunk size in characters (default 1000) */
17
+ chunkSize?: number;
18
+ /** Overlap between consecutive chunks in characters (default 200) */
19
+ chunkOverlap?: number;
20
+ /** Source document identifier used as ID prefix */
21
+ docId?: string | number;
22
+ /** Extra metadata to attach to every chunk */
23
+ metadata?: Record<string, unknown>;
24
+ /** Characters used to split text, in order of priority */
25
+ separators?: string[];
26
+ }
27
+ declare class DocumentChunker {
28
+ private readonly chunkSize;
29
+ private readonly chunkOverlap;
30
+ private readonly separators;
31
+ constructor(chunkSize?: number, chunkOverlap?: number, separators?: string[]);
32
+ /**
33
+ * Split a single text string into overlapping chunks using a recursive strategy.
34
+ * Preserves structural boundaries (Markdown headers) where possible.
35
+ */
36
+ chunk(text: string, options?: ChunkOptions): Chunk[];
37
+ private recursiveSplit;
38
+ chunkMany(documents: Array<{
39
+ content: string;
40
+ docId?: string | number;
41
+ metadata?: Record<string, unknown>;
42
+ }>): Chunk[];
43
+ }
44
+
45
+ export { type Chunk as C, DocumentChunker as D, type ChunkOptions as a };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * DocumentChunker — splits long text into overlapping chunks
3
+ * suitable for vector embedding and retrieval.
4
+ *
5
+ * Strategy: sentence-aware sliding window
6
+ * 1. Split on sentence boundaries
7
+ * 2. Accumulate sentences into chunks up to `chunkSize` characters
8
+ * 3. Carry over `chunkOverlap` characters from the previous chunk
9
+ */
10
+ interface Chunk {
11
+ id: string;
12
+ content: string;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ interface ChunkOptions {
16
+ /** Target chunk size in characters (default 1000) */
17
+ chunkSize?: number;
18
+ /** Overlap between consecutive chunks in characters (default 200) */
19
+ chunkOverlap?: number;
20
+ /** Source document identifier used as ID prefix */
21
+ docId?: string | number;
22
+ /** Extra metadata to attach to every chunk */
23
+ metadata?: Record<string, unknown>;
24
+ /** Characters used to split text, in order of priority */
25
+ separators?: string[];
26
+ }
27
+ declare class DocumentChunker {
28
+ private readonly chunkSize;
29
+ private readonly chunkOverlap;
30
+ private readonly separators;
31
+ constructor(chunkSize?: number, chunkOverlap?: number, separators?: string[]);
32
+ /**
33
+ * Split a single text string into overlapping chunks using a recursive strategy.
34
+ * Preserves structural boundaries (Markdown headers) where possible.
35
+ */
36
+ chunk(text: string, options?: ChunkOptions): Chunk[];
37
+ private recursiveSplit;
38
+ chunkMany(documents: Array<{
39
+ content: string;
40
+ docId?: string | number;
41
+ metadata?: Record<string, unknown>;
42
+ }>): Chunk[];
43
+ }
44
+
45
+ export { type Chunk as C, DocumentChunker as D, type ChunkOptions as a };
@@ -1,3 +1,60 @@
1
+ /**
2
+ * Generic LLM Provider interface.
3
+ * Covers both chat completion and embedding generation so a single
4
+ * provider can handle both responsibilities when appropriate.
5
+ */
6
+ interface ChatMessage {
7
+ role: 'user' | 'assistant' | 'system';
8
+ content: string;
9
+ }
10
+ interface ChatOptions {
11
+ /** Override max tokens for this specific call */
12
+ maxTokens?: number;
13
+ /** Override temperature for this specific call */
14
+ temperature?: number;
15
+ /** Stop sequences */
16
+ stop?: string[];
17
+ }
18
+ interface EmbedOptions {
19
+ /** Override model for this specific embed call */
20
+ model?: string;
21
+ /** Specify the task type for models that require prefixes (e.g. nomic-embed-text) */
22
+ taskType?: 'query' | 'document';
23
+ }
24
+ interface ILLMProvider {
25
+ /**
26
+ * Send a chat completion request.
27
+ * @param messages – the full conversation history
28
+ * @param context – retrieved RAG context to inject
29
+ * @param options – optional per-call overrides
30
+ * @returns – the assistant's reply text
31
+ */
32
+ chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
33
+ /**
34
+ * Send a streaming chat completion request.
35
+ * @returns – an async iterable of text chunks
36
+ */
37
+ chatStream?(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
38
+ /**
39
+ * Generate an embedding vector for the given text.
40
+ * @param text – text to embed
41
+ * @param options – optional overrides
42
+ * @returns – float array (the embedding)
43
+ */
44
+ embed(text: string, options?: EmbedOptions): Promise<number[]>;
45
+ /**
46
+ * Generate embedding vectors for multiple texts in a single batch.
47
+ * @param texts – array of texts to embed
48
+ * @param options – optional overrides
49
+ * @returns – array of float arrays
50
+ */
51
+ batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
52
+ /**
53
+ * Check if the provider endpoint is reachable.
54
+ */
55
+ ping(): Promise<boolean>;
56
+ }
57
+
1
58
  interface VectorMatch {
2
59
  id: string | number;
3
60
  score: number;
@@ -208,4 +265,4 @@ interface RagConfig {
208
265
  graphDb?: GraphDBConfig;
209
266
  }
210
267
 
211
- export type { ChatResponse as C, EmbeddingConfig as E, GraphDBConfig as G, IngestDocument as I, LLMConfig as L, RAGConfig as R, UIConfig as U, VectorMatch as V, EmbeddingProvider as a, LLMProvider as b, RagConfig as c, UpsertDocument as d, VectorDBConfig as e, VectorDBProvider as f, RetrievalResult as g, GraphNode as h, Edge as i, GraphSearchResult as j };
268
+ export type { ChatMessage as C, EmbedOptions as E, GraphDBConfig as G, ILLMProvider as I, LLMConfig as L, RAGConfig as R, UIConfig as U, VectorMatch as V, ChatOptions as a, ChatResponse as b, EmbeddingConfig as c, EmbeddingProvider as d, IngestDocument as e, LLMProvider as f, RagConfig as g, UpsertDocument as h, VectorDBConfig as i, VectorDBProvider as j, RetrievalResult as k, GraphNode as l, Edge as m, GraphSearchResult as n };
@@ -1,3 +1,60 @@
1
+ /**
2
+ * Generic LLM Provider interface.
3
+ * Covers both chat completion and embedding generation so a single
4
+ * provider can handle both responsibilities when appropriate.
5
+ */
6
+ interface ChatMessage {
7
+ role: 'user' | 'assistant' | 'system';
8
+ content: string;
9
+ }
10
+ interface ChatOptions {
11
+ /** Override max tokens for this specific call */
12
+ maxTokens?: number;
13
+ /** Override temperature for this specific call */
14
+ temperature?: number;
15
+ /** Stop sequences */
16
+ stop?: string[];
17
+ }
18
+ interface EmbedOptions {
19
+ /** Override model for this specific embed call */
20
+ model?: string;
21
+ /** Specify the task type for models that require prefixes (e.g. nomic-embed-text) */
22
+ taskType?: 'query' | 'document';
23
+ }
24
+ interface ILLMProvider {
25
+ /**
26
+ * Send a chat completion request.
27
+ * @param messages – the full conversation history
28
+ * @param context – retrieved RAG context to inject
29
+ * @param options – optional per-call overrides
30
+ * @returns – the assistant's reply text
31
+ */
32
+ chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
33
+ /**
34
+ * Send a streaming chat completion request.
35
+ * @returns – an async iterable of text chunks
36
+ */
37
+ chatStream?(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
38
+ /**
39
+ * Generate an embedding vector for the given text.
40
+ * @param text – text to embed
41
+ * @param options – optional overrides
42
+ * @returns – float array (the embedding)
43
+ */
44
+ embed(text: string, options?: EmbedOptions): Promise<number[]>;
45
+ /**
46
+ * Generate embedding vectors for multiple texts in a single batch.
47
+ * @param texts – array of texts to embed
48
+ * @param options – optional overrides
49
+ * @returns – array of float arrays
50
+ */
51
+ batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
52
+ /**
53
+ * Check if the provider endpoint is reachable.
54
+ */
55
+ ping(): Promise<boolean>;
56
+ }
57
+
1
58
  interface VectorMatch {
2
59
  id: string | number;
3
60
  score: number;
@@ -208,4 +265,4 @@ interface RagConfig {
208
265
  graphDb?: GraphDBConfig;
209
266
  }
210
267
 
211
- export type { ChatResponse as C, EmbeddingConfig as E, GraphDBConfig as G, IngestDocument as I, LLMConfig as L, RAGConfig as R, UIConfig as U, VectorMatch as V, EmbeddingProvider as a, LLMProvider as b, RagConfig as c, UpsertDocument as d, VectorDBConfig as e, VectorDBProvider as f, RetrievalResult as g, GraphNode as h, Edge as i, GraphSearchResult as j };
268
+ export type { ChatMessage as C, EmbedOptions as E, GraphDBConfig as G, ILLMProvider as I, LLMConfig as L, RAGConfig as R, UIConfig as U, VectorMatch as V, ChatOptions as a, ChatResponse as b, EmbeddingConfig as c, EmbeddingProvider as d, IngestDocument as e, LLMProvider as f, RagConfig as g, UpsertDocument as h, VectorDBConfig as i, VectorDBProvider as j, RetrievalResult as k, GraphNode as l, Edge as m, GraphSearchResult as n };
@@ -190,13 +190,23 @@ function getRagConfig(env = process.env) {
190
190
  }
191
191
 
192
192
  // src/core/ConfigResolver.ts
193
+ var _cachedEnvConfig = null;
194
+ function getCachedEnvConfig() {
195
+ if (!_cachedEnvConfig) {
196
+ _cachedEnvConfig = getRagConfig();
197
+ }
198
+ return _cachedEnvConfig;
199
+ }
200
+ function resetConfigCache() {
201
+ _cachedEnvConfig = null;
202
+ }
193
203
  var ConfigResolver = class {
194
204
  /**
195
- * Resolves the final configuration by merging host-provided config with defaults.
205
+ * Resolves the final configuration by merging host-provided config with environment defaults.
196
206
  * @param hostConfig - Partial configuration passed from the host application.
197
207
  */
198
208
  static resolve(hostConfig) {
199
- const envConfig = getRagConfig();
209
+ const envConfig = getCachedEnvConfig();
200
210
  if (!hostConfig) {
201
211
  return envConfig;
202
212
  }
@@ -1508,14 +1518,16 @@ var LangChainAgent = class {
1508
1518
  this.config = config;
1509
1519
  }
1510
1520
  /**
1511
- * Initializes the agent with the RAG pipeline as a primary tool.
1512
- * Dynamically imports LangChain dependencies to avoid build errors if missing.
1521
+ * Initializes the LangChain ReAct agent with the RAG pipeline as a tool.
1522
+ * Dynamically imports langchain so the package doesn't crash if it isn't installed.
1513
1523
  */
1514
1524
  async initialize(chatModel) {
1515
1525
  try {
1516
- const { DynamicTool } = await import(`${"@langchain/core/tools"}`);
1517
- const { ChatPromptTemplate, MessagesPlaceholder } = await import(`${"@langchain/core/prompts"}`);
1518
- const { AgentExecutor, createOpenAIFunctionsAgent } = await import(`${"langchain/agents"}`);
1526
+ const [{ DynamicTool }, { HumanMessage }, { createAgent }] = await Promise.all([
1527
+ import("@langchain/core/tools"),
1528
+ import("@langchain/core/messages"),
1529
+ import("langchain")
1530
+ ]);
1519
1531
  const searchTool = new DynamicTool({
1520
1532
  name: "document_search",
1521
1533
  description: "Use this tool to search through the knowledge base and document repository. Input should be a specific search query.",
@@ -1524,42 +1536,53 @@ var LangChainAgent = class {
1524
1536
  return `Search Results:
1525
1537
  ${response.reply}
1526
1538
 
1527
- Sources Used: ${JSON.stringify(response.sources.map((s) => s.id))}`;
1539
+ Sources Used: ${JSON.stringify(
1540
+ response.sources.map((s) => s.id)
1541
+ )}`;
1528
1542
  }
1529
1543
  });
1530
- const tools = [searchTool];
1531
- const prompt = ChatPromptTemplate.fromMessages([
1532
- ["system", this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."],
1533
- new MessagesPlaceholder("chat_history"),
1534
- ["human", "{input}"],
1535
- new MessagesPlaceholder("agent_scratchpad")
1536
- ]);
1537
- const agent = await createOpenAIFunctionsAgent({
1538
- llm: chatModel,
1539
- tools,
1540
- prompt
1541
- });
1542
- this.executor = new AgentExecutor({
1543
- agent,
1544
- tools
1544
+ this.agent = createAgent({
1545
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1546
+ model: chatModel,
1547
+ tools: [searchTool],
1548
+ systemPrompt: this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."
1545
1549
  });
1550
+ void HumanMessage;
1546
1551
  } catch (error) {
1547
- console.error("[LangChainAgent] Failed to initialize. Ensure 'langchain' and '@langchain/core' are installed.");
1548
- throw error;
1552
+ const isMissing = error instanceof Error && error.message.includes("Cannot find module");
1553
+ const hint = isMissing ? " Make sure 'langchain' and '@langchain/core' are installed:\n npm install langchain @langchain/core" : "";
1554
+ throw new Error(
1555
+ `[LangChainAgent] Failed to initialize.${hint}
1556
+ ${error instanceof Error ? error.message : String(error)}`
1557
+ );
1549
1558
  }
1550
1559
  }
1551
1560
  /**
1552
1561
  * Run the agentic flow.
1562
+ *
1563
+ * LangChain v1.x: invoke takes `{ messages: [...] }` instead of `{ input, chat_history }`.
1564
+ * The agent returns `{ messages: [...] }` — the last message is the final answer.
1553
1565
  */
1554
1566
  async run(input, chatHistory = []) {
1555
- if (!this.executor) {
1567
+ var _a, _b, _c;
1568
+ if (!this.agent) {
1556
1569
  throw new Error("[LangChainAgent] Agent not initialized. Call initialize() first.");
1557
1570
  }
1558
- const response = await this.executor.invoke({
1559
- input,
1560
- chat_history: chatHistory
1571
+ const { HumanMessage, AIMessage } = await import("@langchain/core/messages");
1572
+ const historyMessages = chatHistory.map(
1573
+ (m) => m.role === "user" ? new HumanMessage(m.content) : new AIMessage(m.content)
1574
+ );
1575
+ const response = await this.agent.invoke({
1576
+ messages: [...historyMessages, new HumanMessage(input)]
1561
1577
  });
1562
- return response.output;
1578
+ const lastMessage = (_a = response == null ? void 0 : response.messages) == null ? void 0 : _a.at(-1);
1579
+ if (lastMessage && typeof lastMessage.content === "string") {
1580
+ return lastMessage.content;
1581
+ }
1582
+ if (Array.isArray(lastMessage == null ? void 0 : lastMessage.content)) {
1583
+ return lastMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("");
1584
+ }
1585
+ return String((_c = (_b = response == null ? void 0 : response.output) != null ? _b : response) != null ? _c : "");
1563
1586
  }
1564
1587
  };
1565
1588
 
@@ -1968,6 +1991,10 @@ var QueryProcessor = class {
1968
1991
  }
1969
1992
  /**
1970
1993
  * Constructs a QueryFilter object from extracted hints.
1994
+ *
1995
+ * Note: proper nouns are extracted inside `extractQueryFieldHints` and surfaced
1996
+ * as field-less hints; `buildQueryFilter` adds them to `keywords` without
1997
+ * re-running the proper noun regex to avoid duplication.
1971
1998
  */
1972
1999
  static buildQueryFilter(question, hints) {
1973
2000
  const filter = { metadata: {}, keywords: [], queryText: question };
@@ -1975,13 +2002,11 @@ var QueryProcessor = class {
1975
2002
  if (hint.field) {
1976
2003
  filter.metadata[hint.field] = hint.value;
1977
2004
  } else {
1978
- filter.keywords.push(hint.value);
2005
+ if (!filter.keywords.includes(hint.value)) {
2006
+ filter.keywords.push(hint.value);
2007
+ }
1979
2008
  }
1980
2009
  }
1981
- for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
1982
- const term = this.normalizeHintValue(match[0]);
1983
- if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
1984
- }
1985
2010
  if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
1986
2011
  if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
1987
2012
  return filter;
@@ -1989,10 +2014,40 @@ var QueryProcessor = class {
1989
2014
  };
1990
2015
 
1991
2016
  // src/core/Pipeline.ts
2017
+ var LRUEmbeddingCache = class {
2018
+ constructor(maxSize = 500) {
2019
+ this.cache = /* @__PURE__ */ new Map();
2020
+ this.maxSize = maxSize;
2021
+ }
2022
+ get(key) {
2023
+ const value = this.cache.get(key);
2024
+ if (value !== void 0) {
2025
+ this.cache.delete(key);
2026
+ this.cache.set(key, value);
2027
+ }
2028
+ return value;
2029
+ }
2030
+ set(key, value) {
2031
+ if (this.cache.has(key)) {
2032
+ this.cache.delete(key);
2033
+ } else if (this.cache.size >= this.maxSize) {
2034
+ const oldestKey = this.cache.keys().next().value;
2035
+ if (oldestKey !== void 0) this.cache.delete(oldestKey);
2036
+ }
2037
+ this.cache.set(key, value);
2038
+ }
2039
+ clear() {
2040
+ this.cache.clear();
2041
+ }
2042
+ get size() {
2043
+ return this.cache.size;
2044
+ }
2045
+ };
1992
2046
  var Pipeline = class {
1993
2047
  constructor(config) {
1994
2048
  this.config = config;
1995
- this.embeddingCache = /* @__PURE__ */ new Map();
2049
+ /** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
2050
+ this.embeddingCache = new LRUEmbeddingCache(500);
1996
2051
  this.initialised = false;
1997
2052
  var _a, _b, _c, _d, _e;
1998
2053
  this.chunker = new DocumentChunker(
@@ -2080,6 +2135,7 @@ var Pipeline = class {
2080
2135
  }
2081
2136
  /**
2082
2137
  * Step 2: Generate embeddings for chunks with retry logic.
2138
+ * Uses batchEmbed when available for efficiency; falls back to sequential embedding.
2083
2139
  */
2084
2140
  async processEmbeddings(chunks) {
2085
2141
  const embedBatchOptions = {
@@ -2093,7 +2149,9 @@ var Pipeline = class {
2093
2149
  embedBatchOptions
2094
2150
  );
2095
2151
  if (vectors.length !== chunks.length) {
2096
- throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
2152
+ throw new Error(
2153
+ `[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
2154
+ );
2097
2155
  }
2098
2156
  return vectors;
2099
2157
  }
@@ -2247,6 +2305,7 @@ ${context}`;
2247
2305
  }
2248
2306
  /**
2249
2307
  * Universal retrieval method combining all enabled providers.
2308
+ * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
2250
2309
  */
2251
2310
  async retrieve(query, options) {
2252
2311
  var _a, _b, _c;
@@ -2279,10 +2338,13 @@ ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
2279
2338
  New Question: ${question}
2280
2339
 
2281
2340
  Optimized Search Query:`;
2282
- const rewrite = await this.llmProvider.chat([
2283
- { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
2284
- { role: "user", content: prompt }
2285
- ], "");
2341
+ const rewrite = await this.llmProvider.chat(
2342
+ [
2343
+ { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
2344
+ { role: "user", content: prompt }
2345
+ ],
2346
+ ""
2347
+ );
2286
2348
  return rewrite.trim() || question;
2287
2349
  }
2288
2350
  };
@@ -2501,8 +2563,35 @@ var DocumentParser = class {
2501
2563
  };
2502
2564
 
2503
2565
  // src/handlers/index.ts
2504
- function createChatHandler(config) {
2505
- const plugin = new VectorPlugin(config);
2566
+ function sseFrame(payload) {
2567
+ return `data: ${JSON.stringify(payload)}
2568
+
2569
+ `;
2570
+ }
2571
+ function sseTextFrame(text) {
2572
+ return `data: ${JSON.stringify({ type: "text", text })}
2573
+
2574
+ `;
2575
+ }
2576
+ function sseMetaFrame(meta) {
2577
+ return `data: ${JSON.stringify(__spreadValues({ type: "metadata" }, meta != null ? meta : {}))}
2578
+
2579
+ `;
2580
+ }
2581
+ function sseErrorFrame(message) {
2582
+ return `data: ${JSON.stringify({ type: "error", error: message })}
2583
+
2584
+ `;
2585
+ }
2586
+ var SSE_HEADERS = {
2587
+ "Content-Type": "text/event-stream; charset=utf-8",
2588
+ "Cache-Control": "no-cache, no-transform",
2589
+ Connection: "keep-alive",
2590
+ "X-Accel-Buffering": "no"
2591
+ // Disable Nginx buffering for streaming
2592
+ };
2593
+ function createChatHandler(configOrPlugin) {
2594
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
2506
2595
  return async function POST(req) {
2507
2596
  try {
2508
2597
  const body = await req.json();
@@ -2518,67 +2607,64 @@ function createChatHandler(config) {
2518
2607
  }
2519
2608
  };
2520
2609
  }
2521
- function createStreamHandler(config) {
2522
- const plugin = new VectorPlugin(config);
2610
+ function createStreamHandler(configOrPlugin) {
2611
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
2523
2612
  return async function POST(req) {
2613
+ let body;
2524
2614
  try {
2525
- const body = await req.json();
2526
- const { message, history = [], namespace } = body;
2527
- const encoder = new TextEncoder();
2528
- const stream = new ReadableStream({
2529
- async start(controller) {
2615
+ body = await req.json();
2616
+ } catch (e) {
2617
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
2618
+ status: 400,
2619
+ headers: { "Content-Type": "application/json" }
2620
+ });
2621
+ }
2622
+ const { message, history = [], namespace } = body;
2623
+ if (!(message == null ? void 0 : message.trim())) {
2624
+ return new Response(JSON.stringify({ error: "message is required" }), {
2625
+ status: 400,
2626
+ headers: { "Content-Type": "application/json" }
2627
+ });
2628
+ }
2629
+ const encoder = new TextEncoder();
2630
+ const stream = new ReadableStream({
2631
+ async start(controller) {
2632
+ const enqueue = (text) => controller.enqueue(encoder.encode(text));
2633
+ try {
2634
+ const pipelineStream = plugin.chatStream(message, history, namespace);
2530
2635
  try {
2531
- const pipelineStream = plugin.chatStream(message, history, namespace);
2532
- try {
2533
- for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
2534
- const chunk = temp.value;
2535
- if (typeof chunk === "string") {
2536
- controller.enqueue(encoder.encode(chunk));
2537
- } else {
2538
- controller.enqueue(encoder.encode(`
2539
-
2540
- __METADATA__${JSON.stringify(chunk)}`));
2541
- }
2636
+ for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
2637
+ const chunk = temp.value;
2638
+ if (typeof chunk === "string") {
2639
+ enqueue(sseTextFrame(chunk));
2640
+ } else {
2641
+ enqueue(sseMetaFrame(chunk));
2542
2642
  }
2543
- } catch (temp) {
2544
- error = [temp];
2643
+ }
2644
+ } catch (temp) {
2645
+ error = [temp];
2646
+ } finally {
2647
+ try {
2648
+ more && (temp = iter.return) && await temp.call(iter);
2545
2649
  } finally {
2546
- try {
2547
- more && (temp = iter.return) && await temp.call(iter);
2548
- } finally {
2549
- if (error)
2550
- throw error[0];
2551
- }
2650
+ if (error)
2651
+ throw error[0];
2552
2652
  }
2553
- controller.close();
2554
- } catch (streamError) {
2555
- console.error("[createStreamHandler] Stream processing error:", streamError);
2556
- const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
2557
- controller.enqueue(encoder.encode(`
2558
-
2559
- __ERROR__${JSON.stringify({ error: errorMessage })}`));
2560
- controller.close();
2561
2653
  }
2654
+ } catch (streamError) {
2655
+ const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
2656
+ console.error("[createStreamHandler] Stream error:", streamError);
2657
+ enqueue(sseErrorFrame(errorMessage));
2658
+ } finally {
2659
+ controller.close();
2562
2660
  }
2563
- });
2564
- return new Response(stream, {
2565
- headers: {
2566
- "Content-Type": "text/event-stream",
2567
- "Cache-Control": "no-cache",
2568
- "Connection": "keep-alive"
2569
- }
2570
- });
2571
- } catch (err) {
2572
- const message = err instanceof Error ? err.message : "Internal server error";
2573
- return new Response(JSON.stringify({ error: message }), {
2574
- status: 500,
2575
- headers: { "Content-Type": "application/json" }
2576
- });
2577
- }
2661
+ }
2662
+ });
2663
+ return new Response(stream, { headers: SSE_HEADERS });
2578
2664
  };
2579
2665
  }
2580
- function createIngestHandler(config) {
2581
- const plugin = new VectorPlugin(config);
2666
+ function createIngestHandler(configOrPlugin) {
2667
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
2582
2668
  return async function POST(req) {
2583
2669
  try {
2584
2670
  const body = await req.json();
@@ -2594,8 +2680,8 @@ function createIngestHandler(config) {
2594
2680
  }
2595
2681
  };
2596
2682
  }
2597
- function createHealthHandler(config) {
2598
- const plugin = new VectorPlugin(config);
2683
+ function createHealthHandler(configOrPlugin) {
2684
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
2599
2685
  return async function GET() {
2600
2686
  try {
2601
2687
  const health = await plugin.checkHealth();
@@ -2611,8 +2697,8 @@ function createHealthHandler(config) {
2611
2697
  }
2612
2698
  };
2613
2699
  }
2614
- function createUploadHandler(config) {
2615
- const plugin = new VectorPlugin(config);
2700
+ function createUploadHandler(configOrPlugin) {
2701
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
2616
2702
  return async function POST(req) {
2617
2703
  try {
2618
2704
  const formData = await req.formData();
@@ -2647,6 +2733,7 @@ function createUploadHandler(config) {
2647
2733
 
2648
2734
  export {
2649
2735
  getRagConfig,
2736
+ resetConfigCache,
2650
2737
  ConfigResolver,
2651
2738
  OpenAIProvider,
2652
2739
  AnthropicProvider,
@@ -2665,6 +2752,10 @@ export {
2665
2752
  ProviderHealthCheck,
2666
2753
  VectorPlugin,
2667
2754
  DocumentParser,
2755
+ sseFrame,
2756
+ sseTextFrame,
2757
+ sseMetaFrame,
2758
+ sseErrorFrame,
2668
2759
  createChatHandler,
2669
2760
  createStreamHandler,
2670
2761
  createIngestHandler,
@@ -1,3 +1,3 @@
1
- import '../RagConfig-DRJO4hGU.mjs';
2
- export { c as createChatHandler, b as createHealthHandler, d as createIngestHandler, f as createStreamHandler, e as createUploadHandler } from '../index-Bjy0es5a.mjs';
1
+ import '../RagConfig-BEmz4hVP.mjs';
2
+ export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-DFSy0CG6.mjs';
3
3
  import 'next/server';
@@ -1,3 +1,3 @@
1
- import '../RagConfig-DRJO4hGU.js';
2
- export { c as createChatHandler, b as createHealthHandler, d as createIngestHandler, f as createStreamHandler, e as createUploadHandler } from '../index-B2mutkgp.js';
1
+ import '../RagConfig-BEmz4hVP.js';
2
+ export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-CSfaCeux.js';
3
3
  import 'next/server';