@retrivora-ai/rag-engine 1.0.3 → 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 (40) 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-3JR3SAWX.mjs → chunk-MWL4AGQI.mjs} +216 -108
  6. package/dist/handlers/index.d.mts +2 -2
  7. package/dist/handlers/index.d.ts +2 -2
  8. package/dist/handlers/index.js +218 -110
  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 +298 -225
  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/rag/EntityExtractor.ts +40 -13
  36. package/src/server.ts +12 -2
  37. package/dist/DocumentChunker-C-sCZPhi.d.mts +0 -102
  38. package/dist/DocumentChunker-C-sCZPhi.d.ts +0 -102
  39. package/dist/index-B2mutkgp.d.ts +0 -116
  40. package/dist/index-Bjy0es5a.d.mts +0 -116
package/dist/server.js CHANGED
@@ -1551,8 +1551,14 @@ __export(server_exports, {
1551
1551
  createFromPreset: () => createFromPreset,
1552
1552
  createHealthHandler: () => createHealthHandler,
1553
1553
  createIngestHandler: () => createIngestHandler,
1554
+ createStreamHandler: () => createStreamHandler,
1554
1555
  createUploadHandler: () => createUploadHandler,
1555
- getRagConfig: () => getRagConfig
1556
+ getRagConfig: () => getRagConfig,
1557
+ resetConfigCache: () => resetConfigCache,
1558
+ sseErrorFrame: () => sseErrorFrame,
1559
+ sseFrame: () => sseFrame,
1560
+ sseMetaFrame: () => sseMetaFrame,
1561
+ sseTextFrame: () => sseTextFrame
1556
1562
  });
1557
1563
  module.exports = __toCommonJS(server_exports);
1558
1564
 
@@ -1734,13 +1740,23 @@ function getRagConfig(env = process.env) {
1734
1740
  }
1735
1741
 
1736
1742
  // src/core/ConfigResolver.ts
1743
+ var _cachedEnvConfig = null;
1744
+ function getCachedEnvConfig() {
1745
+ if (!_cachedEnvConfig) {
1746
+ _cachedEnvConfig = getRagConfig();
1747
+ }
1748
+ return _cachedEnvConfig;
1749
+ }
1750
+ function resetConfigCache() {
1751
+ _cachedEnvConfig = null;
1752
+ }
1737
1753
  var ConfigResolver = class {
1738
1754
  /**
1739
- * Resolves the final configuration by merging host-provided config with defaults.
1755
+ * Resolves the final configuration by merging host-provided config with environment defaults.
1740
1756
  * @param hostConfig - Partial configuration passed from the host application.
1741
1757
  */
1742
1758
  static resolve(hostConfig) {
1743
- const envConfig = getRagConfig();
1759
+ const envConfig = getCachedEnvConfig();
1744
1760
  if (!hostConfig) {
1745
1761
  return envConfig;
1746
1762
  }
@@ -2953,25 +2969,42 @@ Text to extract from:
2953
2969
  const response = await this.llm.chat([
2954
2970
  { role: "system", content: "You are a precise knowledge graph extraction engine. You always output valid JSON and nothing else." },
2955
2971
  { role: "user", content: prompt }
2956
- ], "", { maxTokens: 2048, temperature: 0.1 });
2972
+ ], "", { maxTokens: 4096, temperature: 0.1 });
2957
2973
  try {
2958
2974
  const jsonMatch = response.match(/\{[\s\S]*\}/);
2959
2975
  const cleanJson = jsonMatch ? jsonMatch[0] : response.trim();
2960
2976
  return JSON.parse(cleanJson);
2961
2977
  } catch (e) {
2962
- console.warn(`[EntityExtractor] Failed to parse LLM response. Length: ${response.length} chars.`);
2963
- if (response.trim().endsWith("}") === false && response.includes('"nodes"')) {
2964
- try {
2965
- const partialResponse = response.trim() + (response.includes("[") ? "]}" : "}");
2966
- const jsonMatch = partialResponse.match(/\{[\s\S]*\}/);
2967
- if (jsonMatch) return JSON.parse(jsonMatch[0]);
2968
- } catch (e2) {
2969
- }
2978
+ try {
2979
+ const repaired = this.repairTruncatedJson(response);
2980
+ if (repaired) return JSON.parse(repaired);
2981
+ } catch (e2) {
2970
2982
  }
2971
- console.warn("[EntityExtractor] Full failing response:", response.substring(0, 200) + "...");
2983
+ console.warn(`[EntityExtractor] Failed to parse LLM response. Length: ${response.length} chars.`);
2984
+ console.warn("[EntityExtractor] Snippet:", response.substring(0, 100) + "...");
2972
2985
  return { nodes: [], edges: [] };
2973
2986
  }
2974
2987
  }
2988
+ /**
2989
+ * Attempts to fix JSON that was cut off mid-generation.
2990
+ * Strategy: Find the last valid element in an array and close the structure.
2991
+ */
2992
+ repairTruncatedJson(json) {
2993
+ let text = json.trim();
2994
+ const startIdx = text.indexOf("{");
2995
+ if (startIdx === -1) return null;
2996
+ text = text.substring(startIdx);
2997
+ const lastCompleteObjectIdx = text.lastIndexOf("}");
2998
+ if (lastCompleteObjectIdx === -1) return null;
2999
+ let repaired = text.substring(0, lastCompleteObjectIdx + 1);
3000
+ const openBrackets = (repaired.match(/\{/g) || []).length;
3001
+ const closedBrackets = (repaired.match(/\}/g) || []).length;
3002
+ const openSquares = (repaired.match(/\[/g) || []).length;
3003
+ const closedSquares = (repaired.match(/\]/g) || []).length;
3004
+ for (let i = 0; i < openSquares - closedSquares; i++) repaired += "]";
3005
+ for (let i = 0; i < openBrackets - closedBrackets; i++) repaired += "}";
3006
+ return repaired;
3007
+ }
2975
3008
  };
2976
3009
 
2977
3010
  // src/rag/Reranker.ts
@@ -3036,14 +3069,16 @@ var LangChainAgent = class {
3036
3069
  this.config = config;
3037
3070
  }
3038
3071
  /**
3039
- * Initializes the agent with the RAG pipeline as a primary tool.
3040
- * Dynamically imports LangChain dependencies to avoid build errors if missing.
3072
+ * Initializes the LangChain ReAct agent with the RAG pipeline as a tool.
3073
+ * Dynamically imports langchain so the package doesn't crash if it isn't installed.
3041
3074
  */
3042
3075
  async initialize(chatModel) {
3043
3076
  try {
3044
- const { DynamicTool } = await import(`${"@langchain/core/tools"}`);
3045
- const { ChatPromptTemplate, MessagesPlaceholder } = await import(`${"@langchain/core/prompts"}`);
3046
- const { AgentExecutor, createOpenAIFunctionsAgent } = await import(`${"langchain/agents"}`);
3077
+ const [{ DynamicTool }, { HumanMessage }, { createAgent }] = await Promise.all([
3078
+ import("@langchain/core/tools"),
3079
+ import("@langchain/core/messages"),
3080
+ import("langchain")
3081
+ ]);
3047
3082
  const searchTool = new DynamicTool({
3048
3083
  name: "document_search",
3049
3084
  description: "Use this tool to search through the knowledge base and document repository. Input should be a specific search query.",
@@ -3052,42 +3087,53 @@ var LangChainAgent = class {
3052
3087
  return `Search Results:
3053
3088
  ${response.reply}
3054
3089
 
3055
- Sources Used: ${JSON.stringify(response.sources.map((s) => s.id))}`;
3090
+ Sources Used: ${JSON.stringify(
3091
+ response.sources.map((s) => s.id)
3092
+ )}`;
3056
3093
  }
3057
3094
  });
3058
- const tools = [searchTool];
3059
- const prompt = ChatPromptTemplate.fromMessages([
3060
- ["system", this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."],
3061
- new MessagesPlaceholder("chat_history"),
3062
- ["human", "{input}"],
3063
- new MessagesPlaceholder("agent_scratchpad")
3064
- ]);
3065
- const agent = await createOpenAIFunctionsAgent({
3066
- llm: chatModel,
3067
- tools,
3068
- prompt
3069
- });
3070
- this.executor = new AgentExecutor({
3071
- agent,
3072
- tools
3095
+ this.agent = createAgent({
3096
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3097
+ model: chatModel,
3098
+ tools: [searchTool],
3099
+ systemPrompt: this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."
3073
3100
  });
3101
+ void HumanMessage;
3074
3102
  } catch (error) {
3075
- console.error("[LangChainAgent] Failed to initialize. Ensure 'langchain' and '@langchain/core' are installed.");
3076
- throw error;
3103
+ const isMissing = error instanceof Error && error.message.includes("Cannot find module");
3104
+ const hint = isMissing ? " Make sure 'langchain' and '@langchain/core' are installed:\n npm install langchain @langchain/core" : "";
3105
+ throw new Error(
3106
+ `[LangChainAgent] Failed to initialize.${hint}
3107
+ ${error instanceof Error ? error.message : String(error)}`
3108
+ );
3077
3109
  }
3078
3110
  }
3079
3111
  /**
3080
3112
  * Run the agentic flow.
3113
+ *
3114
+ * LangChain v1.x: invoke takes `{ messages: [...] }` instead of `{ input, chat_history }`.
3115
+ * The agent returns `{ messages: [...] }` — the last message is the final answer.
3081
3116
  */
3082
3117
  async run(input, chatHistory = []) {
3083
- if (!this.executor) {
3118
+ var _a, _b, _c;
3119
+ if (!this.agent) {
3084
3120
  throw new Error("[LangChainAgent] Agent not initialized. Call initialize() first.");
3085
3121
  }
3086
- const response = await this.executor.invoke({
3087
- input,
3088
- chat_history: chatHistory
3122
+ const { HumanMessage, AIMessage } = await import("@langchain/core/messages");
3123
+ const historyMessages = chatHistory.map(
3124
+ (m) => m.role === "user" ? new HumanMessage(m.content) : new AIMessage(m.content)
3125
+ );
3126
+ const response = await this.agent.invoke({
3127
+ messages: [...historyMessages, new HumanMessage(input)]
3089
3128
  });
3090
- return response.output;
3129
+ const lastMessage = (_a = response == null ? void 0 : response.messages) == null ? void 0 : _a.at(-1);
3130
+ if (lastMessage && typeof lastMessage.content === "string") {
3131
+ return lastMessage.content;
3132
+ }
3133
+ if (Array.isArray(lastMessage == null ? void 0 : lastMessage.content)) {
3134
+ return lastMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("");
3135
+ }
3136
+ return String((_c = (_b = response == null ? void 0 : response.output) != null ? _b : response) != null ? _c : "");
3091
3137
  }
3092
3138
  };
3093
3139
 
@@ -3496,6 +3542,10 @@ var QueryProcessor = class {
3496
3542
  }
3497
3543
  /**
3498
3544
  * Constructs a QueryFilter object from extracted hints.
3545
+ *
3546
+ * Note: proper nouns are extracted inside `extractQueryFieldHints` and surfaced
3547
+ * as field-less hints; `buildQueryFilter` adds them to `keywords` without
3548
+ * re-running the proper noun regex to avoid duplication.
3499
3549
  */
3500
3550
  static buildQueryFilter(question, hints) {
3501
3551
  const filter = { metadata: {}, keywords: [], queryText: question };
@@ -3503,13 +3553,11 @@ var QueryProcessor = class {
3503
3553
  if (hint.field) {
3504
3554
  filter.metadata[hint.field] = hint.value;
3505
3555
  } else {
3506
- filter.keywords.push(hint.value);
3556
+ if (!filter.keywords.includes(hint.value)) {
3557
+ filter.keywords.push(hint.value);
3558
+ }
3507
3559
  }
3508
3560
  }
3509
- for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
3510
- const term = this.normalizeHintValue(match[0]);
3511
- if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
3512
- }
3513
3561
  if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
3514
3562
  if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
3515
3563
  return filter;
@@ -3517,10 +3565,40 @@ var QueryProcessor = class {
3517
3565
  };
3518
3566
 
3519
3567
  // src/core/Pipeline.ts
3568
+ var LRUEmbeddingCache = class {
3569
+ constructor(maxSize = 500) {
3570
+ this.cache = /* @__PURE__ */ new Map();
3571
+ this.maxSize = maxSize;
3572
+ }
3573
+ get(key) {
3574
+ const value = this.cache.get(key);
3575
+ if (value !== void 0) {
3576
+ this.cache.delete(key);
3577
+ this.cache.set(key, value);
3578
+ }
3579
+ return value;
3580
+ }
3581
+ set(key, value) {
3582
+ if (this.cache.has(key)) {
3583
+ this.cache.delete(key);
3584
+ } else if (this.cache.size >= this.maxSize) {
3585
+ const oldestKey = this.cache.keys().next().value;
3586
+ if (oldestKey !== void 0) this.cache.delete(oldestKey);
3587
+ }
3588
+ this.cache.set(key, value);
3589
+ }
3590
+ clear() {
3591
+ this.cache.clear();
3592
+ }
3593
+ get size() {
3594
+ return this.cache.size;
3595
+ }
3596
+ };
3520
3597
  var Pipeline = class {
3521
3598
  constructor(config) {
3522
3599
  this.config = config;
3523
- this.embeddingCache = /* @__PURE__ */ new Map();
3600
+ /** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
3601
+ this.embeddingCache = new LRUEmbeddingCache(500);
3524
3602
  this.initialised = false;
3525
3603
  var _a, _b, _c, _d, _e;
3526
3604
  this.chunker = new DocumentChunker(
@@ -3608,6 +3686,7 @@ var Pipeline = class {
3608
3686
  }
3609
3687
  /**
3610
3688
  * Step 2: Generate embeddings for chunks with retry logic.
3689
+ * Uses batchEmbed when available for efficiency; falls back to sequential embedding.
3611
3690
  */
3612
3691
  async processEmbeddings(chunks) {
3613
3692
  const embedBatchOptions = {
@@ -3621,7 +3700,9 @@ var Pipeline = class {
3621
3700
  embedBatchOptions
3622
3701
  );
3623
3702
  if (vectors.length !== chunks.length) {
3624
- throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
3703
+ throw new Error(
3704
+ `[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
3705
+ );
3625
3706
  }
3626
3707
  return vectors;
3627
3708
  }
@@ -3775,6 +3856,7 @@ ${context}`;
3775
3856
  }
3776
3857
  /**
3777
3858
  * Universal retrieval method combining all enabled providers.
3859
+ * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
3778
3860
  */
3779
3861
  async retrieve(query, options) {
3780
3862
  var _a, _b, _c;
@@ -3807,10 +3889,13 @@ ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
3807
3889
  New Question: ${question}
3808
3890
 
3809
3891
  Optimized Search Query:`;
3810
- const rewrite = await this.llmProvider.chat([
3811
- { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
3812
- { role: "user", content: prompt }
3813
- ], "");
3892
+ const rewrite = await this.llmProvider.chat(
3893
+ [
3894
+ { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
3895
+ { role: "user", content: prompt }
3896
+ ],
3897
+ ""
3898
+ );
3814
3899
  return rewrite.trim() || question;
3815
3900
  }
3816
3901
  };
@@ -3971,21 +4056,11 @@ var VectorPlugin = class {
3971
4056
 
3972
4057
  // src/config/ConfigBuilder.ts
3973
4058
  var ConfigBuilder = class {
3974
- constructor() {
3975
- this.config = {
3976
- projectId: "default-project",
3977
- rag: {
3978
- chunkSize: 1e3,
3979
- chunkOverlap: 200,
3980
- topK: 5
3981
- }
3982
- };
3983
- }
3984
4059
  /**
3985
4060
  * Set the project/application ID for namespacing
3986
4061
  */
3987
4062
  projectId(id) {
3988
- this.config.projectId = id;
4063
+ this._projectId = id;
3989
4064
  return this;
3990
4065
  }
3991
4066
  /**
@@ -3994,9 +4069,9 @@ var ConfigBuilder = class {
3994
4069
  vectorDb(provider, options) {
3995
4070
  var _a;
3996
4071
  if (provider === "auto") {
3997
- this.config.vectorDb = this.autoDetectVectorDb();
4072
+ this._vectorDb = this._autoDetectVectorDb();
3998
4073
  } else {
3999
- this.config.vectorDb = {
4074
+ this._vectorDb = {
4000
4075
  provider,
4001
4076
  indexName: (_a = options == null ? void 0 : options.indexName) != null ? _a : "default",
4002
4077
  options: __spreadValues({}, options)
@@ -4010,9 +4085,9 @@ var ConfigBuilder = class {
4010
4085
  llm(provider, model, apiKey, options) {
4011
4086
  var _a, _b;
4012
4087
  if (provider === "auto") {
4013
- this.config.llm = this.autoDetectLLM();
4088
+ this._llm = this._autoDetectLLM();
4014
4089
  } else {
4015
- this.config.llm = {
4090
+ this._llm = {
4016
4091
  provider,
4017
4092
  model: model != null ? model : "default-model",
4018
4093
  apiKey,
@@ -4030,9 +4105,9 @@ var ConfigBuilder = class {
4030
4105
  */
4031
4106
  embedding(provider, model, apiKey, options) {
4032
4107
  if (provider === "auto") {
4033
- this.config.embedding = this.autoDetectEmbedding();
4108
+ this._embedding = this._autoDetectEmbedding();
4034
4109
  } else {
4035
- this.config.embedding = {
4110
+ this._embedding = {
4036
4111
  provider,
4037
4112
  model: model != null ? model : "default-embedding",
4038
4113
  apiKey,
@@ -4043,26 +4118,46 @@ var ConfigBuilder = class {
4043
4118
  return this;
4044
4119
  }
4045
4120
  /**
4046
- * Set RAG-specific parameters
4121
+ * Set RAG-specific pipeline parameters
4047
4122
  */
4048
4123
  rag(options) {
4049
4124
  var _a;
4050
- this.config.rag = __spreadValues(__spreadValues({}, (_a = this.config.rag) != null ? _a : {}), options);
4125
+ this._rag = __spreadValues(__spreadValues({}, (_a = this._rag) != null ? _a : {}), options);
4051
4126
  return this;
4052
4127
  }
4053
4128
  /**
4054
- * Set UI/UX parameters
4129
+ * Set UI branding and appearance options.
4130
+ * Accepts the full UIConfig interface.
4055
4131
  */
4056
4132
  ui(options) {
4057
- this.config.ui = options;
4133
+ this._ui = options;
4058
4134
  return this;
4059
4135
  }
4060
4136
  /**
4061
- * Build and return the configuration
4137
+ * Build and return the final RagConfig.
4138
+ * Throws if required fields (projectId, vectorDb, llm, embedding) are not set.
4062
4139
  */
4063
4140
  build() {
4064
- const finalConfig = this.config;
4065
- return finalConfig;
4141
+ var _a;
4142
+ const missing = [];
4143
+ if (!this._projectId) missing.push('projectId (call .projectId("my-app"))');
4144
+ if (!this._vectorDb) missing.push('vectorDb (call .vectorDb("pinecone", { ... }))');
4145
+ if (!this._llm) missing.push('llm (call .llm("openai", "gpt-4o", apiKey))');
4146
+ if (!this._embedding) missing.push('embedding (call .embedding("openai", "text-embedding-3-small"))');
4147
+ if (missing.length > 0) {
4148
+ throw new Error(
4149
+ `[ConfigBuilder] Cannot build \u2014 required fields are missing:
4150
+ ${missing.join("\n ")}`
4151
+ );
4152
+ }
4153
+ return __spreadValues({
4154
+ projectId: this._projectId,
4155
+ vectorDb: this._vectorDb,
4156
+ llm: this._llm,
4157
+ embedding: this._embedding,
4158
+ ui: this._rag ? this._ui : void 0,
4159
+ rag: (_a = this._rag) != null ? _a : { chunkSize: 1e3, chunkOverlap: 200, topK: 5 }
4160
+ }, this._ui ? { ui: this._ui } : {});
4066
4161
  }
4067
4162
  /**
4068
4163
  * Build and return as JSON for serialization
@@ -4073,169 +4168,58 @@ var ConfigBuilder = class {
4073
4168
  // ============================================================================
4074
4169
  // Private helper methods for auto-detection
4075
4170
  // ============================================================================
4076
- autoDetectVectorDb() {
4171
+ _autoDetectVectorDb() {
4077
4172
  if (process.env.PINECONE_API_KEY && process.env.PINECONE_INDEX) {
4078
- return {
4079
- provider: "pinecone",
4080
- indexName: process.env.PINECONE_INDEX,
4081
- options: { apiKey: process.env.PINECONE_API_KEY }
4082
- };
4173
+ return { provider: "pinecone", indexName: process.env.PINECONE_INDEX, options: { apiKey: process.env.PINECONE_API_KEY } };
4083
4174
  }
4084
4175
  if (process.env.QDRANT_URL) {
4085
- return {
4086
- provider: "qdrant",
4087
- indexName: process.env.QDRANT_COLLECTION || "documents",
4088
- options: {
4089
- url: process.env.QDRANT_URL,
4090
- apiKey: process.env.QDRANT_API_KEY
4091
- }
4092
- };
4176
+ return { provider: "qdrant", indexName: process.env.QDRANT_COLLECTION || "documents", options: { url: process.env.QDRANT_URL, apiKey: process.env.QDRANT_API_KEY } };
4093
4177
  }
4094
4178
  if (process.env.DATABASE_URL) {
4095
- return {
4096
- provider: "postgresql",
4097
- indexName: process.env.PG_TABLE || "documents",
4098
- options: {
4099
- connectionString: process.env.DATABASE_URL
4100
- }
4101
- };
4179
+ return { provider: "postgresql", indexName: process.env.PG_TABLE || "documents", options: { connectionString: process.env.DATABASE_URL } };
4102
4180
  }
4103
4181
  if (process.env.MONGODB_URI) {
4104
- return {
4105
- provider: "mongodb",
4106
- indexName: process.env.MONGODB_COLLECTION || "documents",
4107
- options: {
4108
- uri: process.env.MONGODB_URI,
4109
- database: process.env.MONGODB_DB || "ai_db",
4110
- collection: process.env.MONGODB_COLLECTION || "documents"
4111
- }
4112
- };
4182
+ return { provider: "mongodb", indexName: process.env.MONGODB_COLLECTION || "documents", options: { uri: process.env.MONGODB_URI, database: process.env.MONGODB_DB || "ai_db", collection: process.env.MONGODB_COLLECTION || "documents" } };
4113
4183
  }
4114
4184
  if (process.env.REDIS_URL) {
4115
- return {
4116
- provider: "redis",
4117
- indexName: process.env.REDIS_INDEX || "documents",
4118
- options: {
4119
- url: process.env.REDIS_URL
4120
- }
4121
- };
4185
+ return { provider: "redis", indexName: process.env.REDIS_INDEX || "documents", options: { url: process.env.REDIS_URL } };
4122
4186
  }
4123
- return {
4124
- provider: "qdrant",
4125
- indexName: "documents",
4126
- options: {
4127
- url: process.env.QDRANT_URL || "http://localhost:6333"
4128
- }
4129
- };
4187
+ return { provider: "qdrant", indexName: "documents", options: { url: process.env.QDRANT_URL || "http://localhost:6333" } };
4130
4188
  }
4131
- autoDetectLLM() {
4189
+ _autoDetectLLM() {
4132
4190
  if (process.env.OPENAI_API_KEY) {
4133
- return {
4134
- provider: "openai",
4135
- model: process.env.OPENAI_MODEL || "gpt-4o-mini",
4136
- apiKey: process.env.OPENAI_API_KEY,
4137
- maxTokens: parseInt(process.env.OPENAI_MAX_TOKENS || "1024"),
4138
- temperature: parseFloat(process.env.OPENAI_TEMPERATURE || "0.7")
4139
- };
4191
+ return { provider: "openai", model: process.env.OPENAI_MODEL || "gpt-4o-mini", apiKey: process.env.OPENAI_API_KEY, maxTokens: parseInt(process.env.OPENAI_MAX_TOKENS || "1024"), temperature: parseFloat(process.env.OPENAI_TEMPERATURE || "0.7") };
4140
4192
  }
4141
4193
  if (process.env.ANTHROPIC_API_KEY) {
4142
- return {
4143
- provider: "anthropic",
4144
- model: process.env.ANTHROPIC_MODEL || "claude-3-haiku-20240307",
4145
- apiKey: process.env.ANTHROPIC_API_KEY,
4146
- maxTokens: parseInt(process.env.ANTHROPIC_MAX_TOKENS || "1024"),
4147
- temperature: parseFloat(process.env.ANTHROPIC_TEMPERATURE || "0.7")
4148
- };
4194
+ return { provider: "anthropic", model: process.env.ANTHROPIC_MODEL || "claude-3-haiku-20240307", apiKey: process.env.ANTHROPIC_API_KEY, maxTokens: parseInt(process.env.ANTHROPIC_MAX_TOKENS || "1024"), temperature: parseFloat(process.env.ANTHROPIC_TEMPERATURE || "0.7") };
4149
4195
  }
4150
4196
  if (process.env.OLLAMA_BASE_URL) {
4151
- return {
4152
- provider: "ollama",
4153
- model: process.env.OLLAMA_MODEL || "mistral",
4154
- baseUrl: process.env.OLLAMA_BASE_URL,
4155
- maxTokens: parseInt(process.env.OLLAMA_MAX_TOKENS || "1024"),
4156
- temperature: parseFloat(process.env.OLLAMA_TEMPERATURE || "0.7")
4157
- };
4197
+ return { provider: "ollama", model: process.env.OLLAMA_MODEL || "mistral", baseUrl: process.env.OLLAMA_BASE_URL, maxTokens: parseInt(process.env.OLLAMA_MAX_TOKENS || "1024"), temperature: parseFloat(process.env.OLLAMA_TEMPERATURE || "0.7") };
4158
4198
  }
4159
- return {
4160
- provider: "openai",
4161
- model: "gpt-4o-mini",
4162
- apiKey: process.env.OPENAI_API_KEY
4163
- };
4199
+ return { provider: "openai", model: "gpt-4o-mini", apiKey: process.env.OPENAI_API_KEY };
4164
4200
  }
4165
- autoDetectEmbedding() {
4201
+ _autoDetectEmbedding() {
4166
4202
  if (process.env.EMBEDDING_PROVIDER) {
4167
- const provider = process.env.EMBEDDING_PROVIDER;
4168
- return {
4169
- provider,
4170
- model: process.env.EMBEDDING_MODEL || "default",
4171
- apiKey: process.env.EMBEDDING_API_KEY,
4172
- baseUrl: process.env.EMBEDDING_BASE_URL
4173
- };
4203
+ return { provider: process.env.EMBEDDING_PROVIDER, model: process.env.EMBEDDING_MODEL || "default", apiKey: process.env.EMBEDDING_API_KEY, baseUrl: process.env.EMBEDDING_BASE_URL };
4174
4204
  }
4175
- return {
4176
- provider: "openai",
4177
- model: "text-embedding-3-small",
4178
- apiKey: process.env.OPENAI_API_KEY
4179
- };
4205
+ return { provider: "openai", model: "text-embedding-3-small", apiKey: process.env.OPENAI_API_KEY };
4180
4206
  }
4181
4207
  };
4182
4208
  var PRESETS = {
4183
- /**
4184
- * OpenAI + Pinecone: Production-ready cloud setup
4185
- */
4186
- "openai-pinecone": {
4187
- vectorDb: "pinecone",
4188
- llm: "openai",
4189
- embedding: "openai"
4190
- },
4191
- /**
4192
- * Claude + Qdrant: Open-source vector DB + proprietary LLM
4193
- */
4194
- "claude-qdrant": {
4195
- vectorDb: "qdrant",
4196
- llm: "anthropic",
4197
- embedding: "openai"
4198
- },
4199
- /**
4200
- * Local development: Ollama + local Qdrant
4201
- */
4202
- "local-dev": {
4203
- vectorDb: "qdrant",
4204
- llm: "ollama",
4205
- embedding: "ollama"
4206
- },
4207
- /**
4208
- * Fully open-source: Ollama LLM + Qdrant vector DB + Ollama embeddings
4209
- */
4210
- "fully-open-source": {
4211
- vectorDb: "qdrant",
4212
- llm: "ollama",
4213
- embedding: "ollama"
4214
- },
4215
- /**
4216
- * PostgreSQL stack: pgvector + OpenAI
4217
- */
4218
- "postgres-openai": {
4219
- vectorDb: "postgresql",
4220
- llm: "openai",
4221
- embedding: "openai"
4222
- },
4223
- /**
4224
- * Enterprise MongoDB: MongoDB Atlas with OpenAI
4225
- */
4226
- "mongodb-openai": {
4227
- vectorDb: "mongodb",
4228
- llm: "openai",
4229
- embedding: "openai"
4230
- },
4231
- /**
4232
- * Redis stack for caching + search
4233
- */
4234
- "redis-openai": {
4235
- vectorDb: "redis",
4236
- llm: "openai",
4237
- embedding: "openai"
4238
- }
4209
+ /** OpenAI + Pinecone: Production-ready cloud setup */
4210
+ "openai-pinecone": { vectorDb: "pinecone", llm: "openai", embedding: "openai" },
4211
+ /** Claude + Qdrant: Open-source vector DB + proprietary LLM */
4212
+ "claude-qdrant": { vectorDb: "qdrant", llm: "anthropic", embedding: "openai" },
4213
+ /** Local development: Ollama + local Qdrant */
4214
+ "local-dev": { vectorDb: "qdrant", llm: "ollama", embedding: "ollama" },
4215
+ /** Fully open-source: Ollama LLM + Qdrant + Ollama embeddings */
4216
+ "fully-open-source": { vectorDb: "qdrant", llm: "ollama", embedding: "ollama" },
4217
+ /** PostgreSQL stack: pgvector + OpenAI */
4218
+ "postgres-openai": { vectorDb: "postgresql", llm: "openai", embedding: "openai" },
4219
+ /** Enterprise MongoDB: MongoDB Atlas with OpenAI */
4220
+ "mongodb-openai": { vectorDb: "mongodb", llm: "openai", embedding: "openai" },
4221
+ /** Redis stack for caching + search */
4222
+ "redis-openai": { vectorDb: "redis", llm: "openai", embedding: "openai" }
4239
4223
  };
4240
4224
  function createFromPreset(presetName) {
4241
4225
  const preset = PRESETS[presetName];
@@ -4514,8 +4498,35 @@ init_UniversalVectorProvider();
4514
4498
 
4515
4499
  // src/handlers/index.ts
4516
4500
  var import_server = require("next/server");
4517
- function createChatHandler(config) {
4518
- const plugin = new VectorPlugin(config);
4501
+ function sseFrame(payload) {
4502
+ return `data: ${JSON.stringify(payload)}
4503
+
4504
+ `;
4505
+ }
4506
+ function sseTextFrame(text) {
4507
+ return `data: ${JSON.stringify({ type: "text", text })}
4508
+
4509
+ `;
4510
+ }
4511
+ function sseMetaFrame(meta) {
4512
+ return `data: ${JSON.stringify(__spreadValues({ type: "metadata" }, meta != null ? meta : {}))}
4513
+
4514
+ `;
4515
+ }
4516
+ function sseErrorFrame(message) {
4517
+ return `data: ${JSON.stringify({ type: "error", error: message })}
4518
+
4519
+ `;
4520
+ }
4521
+ var SSE_HEADERS = {
4522
+ "Content-Type": "text/event-stream; charset=utf-8",
4523
+ "Cache-Control": "no-cache, no-transform",
4524
+ Connection: "keep-alive",
4525
+ "X-Accel-Buffering": "no"
4526
+ // Disable Nginx buffering for streaming
4527
+ };
4528
+ function createChatHandler(configOrPlugin) {
4529
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4519
4530
  return async function POST(req) {
4520
4531
  try {
4521
4532
  const body = await req.json();
@@ -4531,8 +4542,64 @@ function createChatHandler(config) {
4531
4542
  }
4532
4543
  };
4533
4544
  }
4534
- function createIngestHandler(config) {
4535
- const plugin = new VectorPlugin(config);
4545
+ function createStreamHandler(configOrPlugin) {
4546
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4547
+ return async function POST(req) {
4548
+ let body;
4549
+ try {
4550
+ body = await req.json();
4551
+ } catch (e) {
4552
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
4553
+ status: 400,
4554
+ headers: { "Content-Type": "application/json" }
4555
+ });
4556
+ }
4557
+ const { message, history = [], namespace } = body;
4558
+ if (!(message == null ? void 0 : message.trim())) {
4559
+ return new Response(JSON.stringify({ error: "message is required" }), {
4560
+ status: 400,
4561
+ headers: { "Content-Type": "application/json" }
4562
+ });
4563
+ }
4564
+ const encoder = new TextEncoder();
4565
+ const stream = new ReadableStream({
4566
+ async start(controller) {
4567
+ const enqueue = (text) => controller.enqueue(encoder.encode(text));
4568
+ try {
4569
+ const pipelineStream = plugin.chatStream(message, history, namespace);
4570
+ try {
4571
+ for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
4572
+ const chunk = temp.value;
4573
+ if (typeof chunk === "string") {
4574
+ enqueue(sseTextFrame(chunk));
4575
+ } else {
4576
+ enqueue(sseMetaFrame(chunk));
4577
+ }
4578
+ }
4579
+ } catch (temp) {
4580
+ error = [temp];
4581
+ } finally {
4582
+ try {
4583
+ more && (temp = iter.return) && await temp.call(iter);
4584
+ } finally {
4585
+ if (error)
4586
+ throw error[0];
4587
+ }
4588
+ }
4589
+ } catch (streamError) {
4590
+ const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
4591
+ console.error("[createStreamHandler] Stream error:", streamError);
4592
+ enqueue(sseErrorFrame(errorMessage));
4593
+ } finally {
4594
+ controller.close();
4595
+ }
4596
+ }
4597
+ });
4598
+ return new Response(stream, { headers: SSE_HEADERS });
4599
+ };
4600
+ }
4601
+ function createIngestHandler(configOrPlugin) {
4602
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4536
4603
  return async function POST(req) {
4537
4604
  try {
4538
4605
  const body = await req.json();
@@ -4548,8 +4615,8 @@ function createIngestHandler(config) {
4548
4615
  }
4549
4616
  };
4550
4617
  }
4551
- function createHealthHandler(config) {
4552
- const plugin = new VectorPlugin(config);
4618
+ function createHealthHandler(configOrPlugin) {
4619
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4553
4620
  return async function GET() {
4554
4621
  try {
4555
4622
  const health = await plugin.checkHealth();
@@ -4565,8 +4632,8 @@ function createHealthHandler(config) {
4565
4632
  }
4566
4633
  };
4567
4634
  }
4568
- function createUploadHandler(config) {
4569
- const plugin = new VectorPlugin(config);
4635
+ function createUploadHandler(configOrPlugin) {
4636
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4570
4637
  return async function POST(req) {
4571
4638
  try {
4572
4639
  const formData = await req.formData();
@@ -4635,6 +4702,12 @@ function createUploadHandler(config) {
4635
4702
  createFromPreset,
4636
4703
  createHealthHandler,
4637
4704
  createIngestHandler,
4705
+ createStreamHandler,
4638
4706
  createUploadHandler,
4639
- getRagConfig
4707
+ getRagConfig,
4708
+ resetConfigCache,
4709
+ sseErrorFrame,
4710
+ sseFrame,
4711
+ sseMetaFrame,
4712
+ sseTextFrame
4640
4713
  });