@retrivora-ai/rag-engine 1.0.4 → 1.0.6

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-BjC6zSTV.d.mts} +62 -1
  4. package/dist/{RagConfig-DRJO4hGU.d.ts → RagConfig-BjC6zSTV.d.ts} +62 -1
  5. package/dist/{chunk-6MLZHQZT.mjs → chunk-ZCDJSGUW.mjs} +237 -104
  6. package/dist/handlers/index.d.mts +2 -2
  7. package/dist/handlers/index.d.ts +2 -2
  8. package/dist/handlers/index.js +239 -106
  9. package/dist/handlers/index.mjs +11 -3
  10. package/dist/index-C3bLmWcR.d.ts +206 -0
  11. package/dist/index-CU_fQq__.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 +319 -221
  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/RagConfig.ts +4 -0
  28. package/src/config/uiConstants.ts +23 -0
  29. package/src/core/ConfigResolver.ts +57 -29
  30. package/src/core/LangChainAgent.ts +73 -40
  31. package/src/core/Pipeline.ts +64 -8
  32. package/src/core/ProviderRegistry.ts +2 -2
  33. package/src/core/QueryProcessor.ts +45 -12
  34. package/src/handlers/index.ts +138 -49
  35. package/src/hooks/useRagChat.ts +71 -32
  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
  }
@@ -3053,14 +3069,16 @@ var LangChainAgent = class {
3053
3069
  this.config = config;
3054
3070
  }
3055
3071
  /**
3056
- * Initializes the agent with the RAG pipeline as a primary tool.
3057
- * 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.
3058
3074
  */
3059
3075
  async initialize(chatModel) {
3060
3076
  try {
3061
- const { DynamicTool } = await import(`${"@langchain/core/tools"}`);
3062
- const { ChatPromptTemplate, MessagesPlaceholder } = await import(`${"@langchain/core/prompts"}`);
3063
- 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
+ ]);
3064
3082
  const searchTool = new DynamicTool({
3065
3083
  name: "document_search",
3066
3084
  description: "Use this tool to search through the knowledge base and document repository. Input should be a specific search query.",
@@ -3069,42 +3087,53 @@ var LangChainAgent = class {
3069
3087
  return `Search Results:
3070
3088
  ${response.reply}
3071
3089
 
3072
- Sources Used: ${JSON.stringify(response.sources.map((s) => s.id))}`;
3090
+ Sources Used: ${JSON.stringify(
3091
+ response.sources.map((s) => s.id)
3092
+ )}`;
3073
3093
  }
3074
3094
  });
3075
- const tools = [searchTool];
3076
- const prompt = ChatPromptTemplate.fromMessages([
3077
- ["system", this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."],
3078
- new MessagesPlaceholder("chat_history"),
3079
- ["human", "{input}"],
3080
- new MessagesPlaceholder("agent_scratchpad")
3081
- ]);
3082
- const agent = await createOpenAIFunctionsAgent({
3083
- llm: chatModel,
3084
- tools,
3085
- prompt
3086
- });
3087
- this.executor = new AgentExecutor({
3088
- agent,
3089
- 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."
3090
3100
  });
3101
+ void HumanMessage;
3091
3102
  } catch (error) {
3092
- console.error("[LangChainAgent] Failed to initialize. Ensure 'langchain' and '@langchain/core' are installed.");
3093
- 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
+ );
3094
3109
  }
3095
3110
  }
3096
3111
  /**
3097
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.
3098
3116
  */
3099
3117
  async run(input, chatHistory = []) {
3100
- if (!this.executor) {
3118
+ var _a, _b, _c;
3119
+ if (!this.agent) {
3101
3120
  throw new Error("[LangChainAgent] Agent not initialized. Call initialize() first.");
3102
3121
  }
3103
- const response = await this.executor.invoke({
3104
- input,
3105
- 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)]
3106
3128
  });
3107
- 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 : "");
3108
3137
  }
3109
3138
  };
3110
3139
 
@@ -3426,15 +3455,18 @@ var QueryProcessor = class {
3426
3455
  * Checks if a string is likely a question word or common prompt phrase.
3427
3456
  */
3428
3457
  static isLikelyPromptPhrase(value) {
3429
- return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
3458
+ return /^(what|which|who|where|when|why|how|the|is|are|was|were|in|on|at|by|for|with|about|a|an|to|of)\b/i.test(value.trim());
3430
3459
  }
3431
3460
  /**
3432
3461
  * Scans a natural language question for potential metadata hints and keywords.
3462
+ * @param question The user's query
3463
+ * @param validFields Optional list of known filterable fields to look for
3433
3464
  */
3434
- static extractQueryFieldHints(question) {
3465
+ static extractQueryFieldHints(question, validFields = []) {
3435
3466
  var _a, _b, _c, _d;
3436
3467
  if (!question.trim()) return [];
3437
3468
  const hints = /* @__PURE__ */ new Map();
3469
+ const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
3438
3470
  const addHint = (value, field) => {
3439
3471
  const normalizedValue = this.normalizeHintValue(value);
3440
3472
  if (!normalizedValue) return;
@@ -3465,11 +3497,28 @@ var QueryProcessor = class {
3465
3497
  if (name) addHint(name, "name");
3466
3498
  }
3467
3499
  }
3500
+ if (fieldsToSearch.length > 0) {
3501
+ for (const field of fieldsToSearch) {
3502
+ const forwardPattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{2,60})["']?(?=[?.!,]|$)`, "gi");
3503
+ for (const match of question.matchAll(forwardPattern)) {
3504
+ const value = match[1];
3505
+ if (value && !this.isLikelyPromptPhrase(value)) {
3506
+ addHint(value, field);
3507
+ }
3508
+ }
3509
+ const reversePattern = new RegExp(`\\b([^"\\n?.!,\\s]{2,60})\\s+${field}\\b`, "gi");
3510
+ for (const match of question.matchAll(reversePattern)) {
3511
+ const value = match[1];
3512
+ if (value && !this.isLikelyPromptPhrase(value)) {
3513
+ addHint(value, field);
3514
+ }
3515
+ }
3516
+ }
3517
+ }
3468
3518
  const universalPatterns = [
3469
3519
  { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: "email", group: 1 },
3470
3520
  { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: "phone", group: 1 },
3471
3521
  { regex: /(\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2}\/\d{1,2}\/\d{2,4}\b)/g, field: "date", group: 1 },
3472
- { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: "amount", group: 1 },
3473
3522
  { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: "id", group: 2 },
3474
3523
  { regex: /"([^"]{2,120})"/g, group: 1 },
3475
3524
  { regex: /'([^']{2,120})'/g, group: 1 }
@@ -3513,6 +3562,10 @@ var QueryProcessor = class {
3513
3562
  }
3514
3563
  /**
3515
3564
  * Constructs a QueryFilter object from extracted hints.
3565
+ *
3566
+ * Note: proper nouns are extracted inside `extractQueryFieldHints` and surfaced
3567
+ * as field-less hints; `buildQueryFilter` adds them to `keywords` without
3568
+ * re-running the proper noun regex to avoid duplication.
3516
3569
  */
3517
3570
  static buildQueryFilter(question, hints) {
3518
3571
  const filter = { metadata: {}, keywords: [], queryText: question };
@@ -3520,24 +3573,74 @@ var QueryProcessor = class {
3520
3573
  if (hint.field) {
3521
3574
  filter.metadata[hint.field] = hint.value;
3522
3575
  } else {
3523
- filter.keywords.push(hint.value);
3576
+ if (!filter.keywords.includes(hint.value)) {
3577
+ filter.keywords.push(hint.value);
3578
+ }
3524
3579
  }
3525
3580
  }
3526
- for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
3527
- const term = this.normalizeHintValue(match[0]);
3528
- if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
3529
- }
3530
3581
  if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
3531
3582
  if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
3532
3583
  return filter;
3533
3584
  }
3534
3585
  };
3586
+ QueryProcessor.COMMON_METADATA_FIELDS = [
3587
+ "category",
3588
+ "type",
3589
+ "brand",
3590
+ "status",
3591
+ "priority",
3592
+ "id",
3593
+ "name",
3594
+ "email",
3595
+ "phone",
3596
+ "price",
3597
+ "rating",
3598
+ "color",
3599
+ "size",
3600
+ "material",
3601
+ "sku",
3602
+ "role",
3603
+ "department",
3604
+ "location",
3605
+ "tag",
3606
+ "label"
3607
+ ];
3535
3608
 
3536
3609
  // src/core/Pipeline.ts
3610
+ var LRUEmbeddingCache = class {
3611
+ constructor(maxSize = 500) {
3612
+ this.cache = /* @__PURE__ */ new Map();
3613
+ this.maxSize = maxSize;
3614
+ }
3615
+ get(key) {
3616
+ const value = this.cache.get(key);
3617
+ if (value !== void 0) {
3618
+ this.cache.delete(key);
3619
+ this.cache.set(key, value);
3620
+ }
3621
+ return value;
3622
+ }
3623
+ set(key, value) {
3624
+ if (this.cache.has(key)) {
3625
+ this.cache.delete(key);
3626
+ } else if (this.cache.size >= this.maxSize) {
3627
+ const oldestKey = this.cache.keys().next().value;
3628
+ if (oldestKey !== void 0) this.cache.delete(oldestKey);
3629
+ }
3630
+ this.cache.set(key, value);
3631
+ }
3632
+ clear() {
3633
+ this.cache.clear();
3634
+ }
3635
+ get size() {
3636
+ return this.cache.size;
3637
+ }
3638
+ };
3537
3639
  var Pipeline = class {
3538
3640
  constructor(config) {
3539
3641
  this.config = config;
3540
- this.embeddingCache = /* @__PURE__ */ new Map();
3642
+ /** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
3643
+ this.embeddingCache = new LRUEmbeddingCache(500);
3541
3644
  this.initialised = false;
3542
3645
  var _a, _b, _c, _d, _e;
3543
3646
  this.chunker = new DocumentChunker(
@@ -3625,6 +3728,7 @@ var Pipeline = class {
3625
3728
  }
3626
3729
  /**
3627
3730
  * Step 2: Generate embeddings for chunks with retry logic.
3731
+ * Uses batchEmbed when available for efficiency; falls back to sequential embedding.
3628
3732
  */
3629
3733
  async processEmbeddings(chunks) {
3630
3734
  const embedBatchOptions = {
@@ -3638,7 +3742,9 @@ var Pipeline = class {
3638
3742
  embedBatchOptions
3639
3743
  );
3640
3744
  if (vectors.length !== chunks.length) {
3641
- throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
3745
+ throw new Error(
3746
+ `[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
3747
+ );
3642
3748
  }
3643
3749
  return vectors;
3644
3750
  }
@@ -3728,7 +3834,7 @@ var Pipeline = class {
3728
3834
  */
3729
3835
  askStream(_0) {
3730
3836
  return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
3731
- var _a, _b, _c, _d, _e, _f;
3837
+ var _a, _b, _c, _d, _e, _f, _g;
3732
3838
  yield new __await(this.initialize());
3733
3839
  const ns = namespace != null ? namespace : this.config.projectId;
3734
3840
  const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
@@ -3738,7 +3844,7 @@ var Pipeline = class {
3738
3844
  if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
3739
3845
  searchQuery = yield new __await(this.rewriteQuery(question, history));
3740
3846
  }
3741
- const hints = QueryProcessor.extractQueryFieldHints(question);
3847
+ const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
3742
3848
  const filter = QueryProcessor.buildQueryFilter(question, hints);
3743
3849
  const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
3744
3850
  namespace: ns,
@@ -3746,7 +3852,7 @@ var Pipeline = class {
3746
3852
  filter
3747
3853
  }));
3748
3854
  let sources = rawSources.filter((m) => m.score >= scoreThreshold);
3749
- if ((_f = this.config.rag) == null ? void 0 : _f.useReranking) {
3855
+ if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
3750
3856
  sources = yield new __await(this.reranker.rerank(sources, question, topK));
3751
3857
  } else {
3752
3858
  sources = sources.slice(0, topK);
@@ -3792,6 +3898,7 @@ ${context}`;
3792
3898
  }
3793
3899
  /**
3794
3900
  * Universal retrieval method combining all enabled providers.
3901
+ * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
3795
3902
  */
3796
3903
  async retrieve(query, options) {
3797
3904
  var _a, _b, _c;
@@ -3824,10 +3931,13 @@ ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
3824
3931
  New Question: ${question}
3825
3932
 
3826
3933
  Optimized Search Query:`;
3827
- const rewrite = await this.llmProvider.chat([
3828
- { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
3829
- { role: "user", content: prompt }
3830
- ], "");
3934
+ const rewrite = await this.llmProvider.chat(
3935
+ [
3936
+ { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
3937
+ { role: "user", content: prompt }
3938
+ ],
3939
+ ""
3940
+ );
3831
3941
  return rewrite.trim() || question;
3832
3942
  }
3833
3943
  };
@@ -3988,21 +4098,11 @@ var VectorPlugin = class {
3988
4098
 
3989
4099
  // src/config/ConfigBuilder.ts
3990
4100
  var ConfigBuilder = class {
3991
- constructor() {
3992
- this.config = {
3993
- projectId: "default-project",
3994
- rag: {
3995
- chunkSize: 1e3,
3996
- chunkOverlap: 200,
3997
- topK: 5
3998
- }
3999
- };
4000
- }
4001
4101
  /**
4002
4102
  * Set the project/application ID for namespacing
4003
4103
  */
4004
4104
  projectId(id) {
4005
- this.config.projectId = id;
4105
+ this._projectId = id;
4006
4106
  return this;
4007
4107
  }
4008
4108
  /**
@@ -4011,9 +4111,9 @@ var ConfigBuilder = class {
4011
4111
  vectorDb(provider, options) {
4012
4112
  var _a;
4013
4113
  if (provider === "auto") {
4014
- this.config.vectorDb = this.autoDetectVectorDb();
4114
+ this._vectorDb = this._autoDetectVectorDb();
4015
4115
  } else {
4016
- this.config.vectorDb = {
4116
+ this._vectorDb = {
4017
4117
  provider,
4018
4118
  indexName: (_a = options == null ? void 0 : options.indexName) != null ? _a : "default",
4019
4119
  options: __spreadValues({}, options)
@@ -4027,9 +4127,9 @@ var ConfigBuilder = class {
4027
4127
  llm(provider, model, apiKey, options) {
4028
4128
  var _a, _b;
4029
4129
  if (provider === "auto") {
4030
- this.config.llm = this.autoDetectLLM();
4130
+ this._llm = this._autoDetectLLM();
4031
4131
  } else {
4032
- this.config.llm = {
4132
+ this._llm = {
4033
4133
  provider,
4034
4134
  model: model != null ? model : "default-model",
4035
4135
  apiKey,
@@ -4047,9 +4147,9 @@ var ConfigBuilder = class {
4047
4147
  */
4048
4148
  embedding(provider, model, apiKey, options) {
4049
4149
  if (provider === "auto") {
4050
- this.config.embedding = this.autoDetectEmbedding();
4150
+ this._embedding = this._autoDetectEmbedding();
4051
4151
  } else {
4052
- this.config.embedding = {
4152
+ this._embedding = {
4053
4153
  provider,
4054
4154
  model: model != null ? model : "default-embedding",
4055
4155
  apiKey,
@@ -4060,26 +4160,46 @@ var ConfigBuilder = class {
4060
4160
  return this;
4061
4161
  }
4062
4162
  /**
4063
- * Set RAG-specific parameters
4163
+ * Set RAG-specific pipeline parameters
4064
4164
  */
4065
4165
  rag(options) {
4066
4166
  var _a;
4067
- this.config.rag = __spreadValues(__spreadValues({}, (_a = this.config.rag) != null ? _a : {}), options);
4167
+ this._rag = __spreadValues(__spreadValues({}, (_a = this._rag) != null ? _a : {}), options);
4068
4168
  return this;
4069
4169
  }
4070
4170
  /**
4071
- * Set UI/UX parameters
4171
+ * Set UI branding and appearance options.
4172
+ * Accepts the full UIConfig interface.
4072
4173
  */
4073
4174
  ui(options) {
4074
- this.config.ui = options;
4175
+ this._ui = options;
4075
4176
  return this;
4076
4177
  }
4077
4178
  /**
4078
- * Build and return the configuration
4179
+ * Build and return the final RagConfig.
4180
+ * Throws if required fields (projectId, vectorDb, llm, embedding) are not set.
4079
4181
  */
4080
4182
  build() {
4081
- const finalConfig = this.config;
4082
- return finalConfig;
4183
+ var _a;
4184
+ const missing = [];
4185
+ if (!this._projectId) missing.push('projectId (call .projectId("my-app"))');
4186
+ if (!this._vectorDb) missing.push('vectorDb (call .vectorDb("pinecone", { ... }))');
4187
+ if (!this._llm) missing.push('llm (call .llm("openai", "gpt-4o", apiKey))');
4188
+ if (!this._embedding) missing.push('embedding (call .embedding("openai", "text-embedding-3-small"))');
4189
+ if (missing.length > 0) {
4190
+ throw new Error(
4191
+ `[ConfigBuilder] Cannot build \u2014 required fields are missing:
4192
+ ${missing.join("\n ")}`
4193
+ );
4194
+ }
4195
+ return __spreadValues({
4196
+ projectId: this._projectId,
4197
+ vectorDb: this._vectorDb,
4198
+ llm: this._llm,
4199
+ embedding: this._embedding,
4200
+ ui: this._rag ? this._ui : void 0,
4201
+ rag: (_a = this._rag) != null ? _a : { chunkSize: 1e3, chunkOverlap: 200, topK: 5 }
4202
+ }, this._ui ? { ui: this._ui } : {});
4083
4203
  }
4084
4204
  /**
4085
4205
  * Build and return as JSON for serialization
@@ -4090,169 +4210,58 @@ var ConfigBuilder = class {
4090
4210
  // ============================================================================
4091
4211
  // Private helper methods for auto-detection
4092
4212
  // ============================================================================
4093
- autoDetectVectorDb() {
4213
+ _autoDetectVectorDb() {
4094
4214
  if (process.env.PINECONE_API_KEY && process.env.PINECONE_INDEX) {
4095
- return {
4096
- provider: "pinecone",
4097
- indexName: process.env.PINECONE_INDEX,
4098
- options: { apiKey: process.env.PINECONE_API_KEY }
4099
- };
4215
+ return { provider: "pinecone", indexName: process.env.PINECONE_INDEX, options: { apiKey: process.env.PINECONE_API_KEY } };
4100
4216
  }
4101
4217
  if (process.env.QDRANT_URL) {
4102
- return {
4103
- provider: "qdrant",
4104
- indexName: process.env.QDRANT_COLLECTION || "documents",
4105
- options: {
4106
- url: process.env.QDRANT_URL,
4107
- apiKey: process.env.QDRANT_API_KEY
4108
- }
4109
- };
4218
+ return { provider: "qdrant", indexName: process.env.QDRANT_COLLECTION || "documents", options: { url: process.env.QDRANT_URL, apiKey: process.env.QDRANT_API_KEY } };
4110
4219
  }
4111
4220
  if (process.env.DATABASE_URL) {
4112
- return {
4113
- provider: "postgresql",
4114
- indexName: process.env.PG_TABLE || "documents",
4115
- options: {
4116
- connectionString: process.env.DATABASE_URL
4117
- }
4118
- };
4221
+ return { provider: "postgresql", indexName: process.env.PG_TABLE || "documents", options: { connectionString: process.env.DATABASE_URL } };
4119
4222
  }
4120
4223
  if (process.env.MONGODB_URI) {
4121
- return {
4122
- provider: "mongodb",
4123
- indexName: process.env.MONGODB_COLLECTION || "documents",
4124
- options: {
4125
- uri: process.env.MONGODB_URI,
4126
- database: process.env.MONGODB_DB || "ai_db",
4127
- collection: process.env.MONGODB_COLLECTION || "documents"
4128
- }
4129
- };
4224
+ 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" } };
4130
4225
  }
4131
4226
  if (process.env.REDIS_URL) {
4132
- return {
4133
- provider: "redis",
4134
- indexName: process.env.REDIS_INDEX || "documents",
4135
- options: {
4136
- url: process.env.REDIS_URL
4137
- }
4138
- };
4227
+ return { provider: "redis", indexName: process.env.REDIS_INDEX || "documents", options: { url: process.env.REDIS_URL } };
4139
4228
  }
4140
- return {
4141
- provider: "qdrant",
4142
- indexName: "documents",
4143
- options: {
4144
- url: process.env.QDRANT_URL || "http://localhost:6333"
4145
- }
4146
- };
4229
+ return { provider: "qdrant", indexName: "documents", options: { url: process.env.QDRANT_URL || "http://localhost:6333" } };
4147
4230
  }
4148
- autoDetectLLM() {
4231
+ _autoDetectLLM() {
4149
4232
  if (process.env.OPENAI_API_KEY) {
4150
- return {
4151
- provider: "openai",
4152
- model: process.env.OPENAI_MODEL || "gpt-4o-mini",
4153
- apiKey: process.env.OPENAI_API_KEY,
4154
- maxTokens: parseInt(process.env.OPENAI_MAX_TOKENS || "1024"),
4155
- temperature: parseFloat(process.env.OPENAI_TEMPERATURE || "0.7")
4156
- };
4233
+ 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") };
4157
4234
  }
4158
4235
  if (process.env.ANTHROPIC_API_KEY) {
4159
- return {
4160
- provider: "anthropic",
4161
- model: process.env.ANTHROPIC_MODEL || "claude-3-haiku-20240307",
4162
- apiKey: process.env.ANTHROPIC_API_KEY,
4163
- maxTokens: parseInt(process.env.ANTHROPIC_MAX_TOKENS || "1024"),
4164
- temperature: parseFloat(process.env.ANTHROPIC_TEMPERATURE || "0.7")
4165
- };
4236
+ 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") };
4166
4237
  }
4167
4238
  if (process.env.OLLAMA_BASE_URL) {
4168
- return {
4169
- provider: "ollama",
4170
- model: process.env.OLLAMA_MODEL || "mistral",
4171
- baseUrl: process.env.OLLAMA_BASE_URL,
4172
- maxTokens: parseInt(process.env.OLLAMA_MAX_TOKENS || "1024"),
4173
- temperature: parseFloat(process.env.OLLAMA_TEMPERATURE || "0.7")
4174
- };
4239
+ 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") };
4175
4240
  }
4176
- return {
4177
- provider: "openai",
4178
- model: "gpt-4o-mini",
4179
- apiKey: process.env.OPENAI_API_KEY
4180
- };
4241
+ return { provider: "openai", model: "gpt-4o-mini", apiKey: process.env.OPENAI_API_KEY };
4181
4242
  }
4182
- autoDetectEmbedding() {
4243
+ _autoDetectEmbedding() {
4183
4244
  if (process.env.EMBEDDING_PROVIDER) {
4184
- const provider = process.env.EMBEDDING_PROVIDER;
4185
- return {
4186
- provider,
4187
- model: process.env.EMBEDDING_MODEL || "default",
4188
- apiKey: process.env.EMBEDDING_API_KEY,
4189
- baseUrl: process.env.EMBEDDING_BASE_URL
4190
- };
4245
+ return { provider: process.env.EMBEDDING_PROVIDER, model: process.env.EMBEDDING_MODEL || "default", apiKey: process.env.EMBEDDING_API_KEY, baseUrl: process.env.EMBEDDING_BASE_URL };
4191
4246
  }
4192
- return {
4193
- provider: "openai",
4194
- model: "text-embedding-3-small",
4195
- apiKey: process.env.OPENAI_API_KEY
4196
- };
4247
+ return { provider: "openai", model: "text-embedding-3-small", apiKey: process.env.OPENAI_API_KEY };
4197
4248
  }
4198
4249
  };
4199
4250
  var PRESETS = {
4200
- /**
4201
- * OpenAI + Pinecone: Production-ready cloud setup
4202
- */
4203
- "openai-pinecone": {
4204
- vectorDb: "pinecone",
4205
- llm: "openai",
4206
- embedding: "openai"
4207
- },
4208
- /**
4209
- * Claude + Qdrant: Open-source vector DB + proprietary LLM
4210
- */
4211
- "claude-qdrant": {
4212
- vectorDb: "qdrant",
4213
- llm: "anthropic",
4214
- embedding: "openai"
4215
- },
4216
- /**
4217
- * Local development: Ollama + local Qdrant
4218
- */
4219
- "local-dev": {
4220
- vectorDb: "qdrant",
4221
- llm: "ollama",
4222
- embedding: "ollama"
4223
- },
4224
- /**
4225
- * Fully open-source: Ollama LLM + Qdrant vector DB + Ollama embeddings
4226
- */
4227
- "fully-open-source": {
4228
- vectorDb: "qdrant",
4229
- llm: "ollama",
4230
- embedding: "ollama"
4231
- },
4232
- /**
4233
- * PostgreSQL stack: pgvector + OpenAI
4234
- */
4235
- "postgres-openai": {
4236
- vectorDb: "postgresql",
4237
- llm: "openai",
4238
- embedding: "openai"
4239
- },
4240
- /**
4241
- * Enterprise MongoDB: MongoDB Atlas with OpenAI
4242
- */
4243
- "mongodb-openai": {
4244
- vectorDb: "mongodb",
4245
- llm: "openai",
4246
- embedding: "openai"
4247
- },
4248
- /**
4249
- * Redis stack for caching + search
4250
- */
4251
- "redis-openai": {
4252
- vectorDb: "redis",
4253
- llm: "openai",
4254
- embedding: "openai"
4255
- }
4251
+ /** OpenAI + Pinecone: Production-ready cloud setup */
4252
+ "openai-pinecone": { vectorDb: "pinecone", llm: "openai", embedding: "openai" },
4253
+ /** Claude + Qdrant: Open-source vector DB + proprietary LLM */
4254
+ "claude-qdrant": { vectorDb: "qdrant", llm: "anthropic", embedding: "openai" },
4255
+ /** Local development: Ollama + local Qdrant */
4256
+ "local-dev": { vectorDb: "qdrant", llm: "ollama", embedding: "ollama" },
4257
+ /** Fully open-source: Ollama LLM + Qdrant + Ollama embeddings */
4258
+ "fully-open-source": { vectorDb: "qdrant", llm: "ollama", embedding: "ollama" },
4259
+ /** PostgreSQL stack: pgvector + OpenAI */
4260
+ "postgres-openai": { vectorDb: "postgresql", llm: "openai", embedding: "openai" },
4261
+ /** Enterprise MongoDB: MongoDB Atlas with OpenAI */
4262
+ "mongodb-openai": { vectorDb: "mongodb", llm: "openai", embedding: "openai" },
4263
+ /** Redis stack for caching + search */
4264
+ "redis-openai": { vectorDb: "redis", llm: "openai", embedding: "openai" }
4256
4265
  };
4257
4266
  function createFromPreset(presetName) {
4258
4267
  const preset = PRESETS[presetName];
@@ -4531,8 +4540,35 @@ init_UniversalVectorProvider();
4531
4540
 
4532
4541
  // src/handlers/index.ts
4533
4542
  var import_server = require("next/server");
4534
- function createChatHandler(config) {
4535
- const plugin = new VectorPlugin(config);
4543
+ function sseFrame(payload) {
4544
+ return `data: ${JSON.stringify(payload)}
4545
+
4546
+ `;
4547
+ }
4548
+ function sseTextFrame(text) {
4549
+ return `data: ${JSON.stringify({ type: "text", text })}
4550
+
4551
+ `;
4552
+ }
4553
+ function sseMetaFrame(meta) {
4554
+ return `data: ${JSON.stringify(__spreadValues({ type: "metadata" }, meta != null ? meta : {}))}
4555
+
4556
+ `;
4557
+ }
4558
+ function sseErrorFrame(message) {
4559
+ return `data: ${JSON.stringify({ type: "error", error: message })}
4560
+
4561
+ `;
4562
+ }
4563
+ var SSE_HEADERS = {
4564
+ "Content-Type": "text/event-stream; charset=utf-8",
4565
+ "Cache-Control": "no-cache, no-transform",
4566
+ Connection: "keep-alive",
4567
+ "X-Accel-Buffering": "no"
4568
+ // Disable Nginx buffering for streaming
4569
+ };
4570
+ function createChatHandler(configOrPlugin) {
4571
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4536
4572
  return async function POST(req) {
4537
4573
  try {
4538
4574
  const body = await req.json();
@@ -4548,8 +4584,64 @@ function createChatHandler(config) {
4548
4584
  }
4549
4585
  };
4550
4586
  }
4551
- function createIngestHandler(config) {
4552
- const plugin = new VectorPlugin(config);
4587
+ function createStreamHandler(configOrPlugin) {
4588
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4589
+ return async function POST(req) {
4590
+ let body;
4591
+ try {
4592
+ body = await req.json();
4593
+ } catch (e) {
4594
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
4595
+ status: 400,
4596
+ headers: { "Content-Type": "application/json" }
4597
+ });
4598
+ }
4599
+ const { message, history = [], namespace } = body;
4600
+ if (!(message == null ? void 0 : message.trim())) {
4601
+ return new Response(JSON.stringify({ error: "message is required" }), {
4602
+ status: 400,
4603
+ headers: { "Content-Type": "application/json" }
4604
+ });
4605
+ }
4606
+ const encoder = new TextEncoder();
4607
+ const stream = new ReadableStream({
4608
+ async start(controller) {
4609
+ const enqueue = (text) => controller.enqueue(encoder.encode(text));
4610
+ try {
4611
+ const pipelineStream = plugin.chatStream(message, history, namespace);
4612
+ try {
4613
+ for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
4614
+ const chunk = temp.value;
4615
+ if (typeof chunk === "string") {
4616
+ enqueue(sseTextFrame(chunk));
4617
+ } else {
4618
+ enqueue(sseMetaFrame(chunk));
4619
+ }
4620
+ }
4621
+ } catch (temp) {
4622
+ error = [temp];
4623
+ } finally {
4624
+ try {
4625
+ more && (temp = iter.return) && await temp.call(iter);
4626
+ } finally {
4627
+ if (error)
4628
+ throw error[0];
4629
+ }
4630
+ }
4631
+ } catch (streamError) {
4632
+ const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
4633
+ console.error("[createStreamHandler] Stream error:", streamError);
4634
+ enqueue(sseErrorFrame(errorMessage));
4635
+ } finally {
4636
+ controller.close();
4637
+ }
4638
+ }
4639
+ });
4640
+ return new Response(stream, { headers: SSE_HEADERS });
4641
+ };
4642
+ }
4643
+ function createIngestHandler(configOrPlugin) {
4644
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4553
4645
  return async function POST(req) {
4554
4646
  try {
4555
4647
  const body = await req.json();
@@ -4565,8 +4657,8 @@ function createIngestHandler(config) {
4565
4657
  }
4566
4658
  };
4567
4659
  }
4568
- function createHealthHandler(config) {
4569
- const plugin = new VectorPlugin(config);
4660
+ function createHealthHandler(configOrPlugin) {
4661
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4570
4662
  return async function GET() {
4571
4663
  try {
4572
4664
  const health = await plugin.checkHealth();
@@ -4582,8 +4674,8 @@ function createHealthHandler(config) {
4582
4674
  }
4583
4675
  };
4584
4676
  }
4585
- function createUploadHandler(config) {
4586
- const plugin = new VectorPlugin(config);
4677
+ function createUploadHandler(configOrPlugin) {
4678
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4587
4679
  return async function POST(req) {
4588
4680
  try {
4589
4681
  const formData = await req.formData();
@@ -4652,6 +4744,12 @@ function createUploadHandler(config) {
4652
4744
  createFromPreset,
4653
4745
  createHealthHandler,
4654
4746
  createIngestHandler,
4747
+ createStreamHandler,
4655
4748
  createUploadHandler,
4656
- getRagConfig
4749
+ getRagConfig,
4750
+ resetConfigCache,
4751
+ sseErrorFrame,
4752
+ sseFrame,
4753
+ sseMetaFrame,
4754
+ sseTextFrame
4657
4755
  });