@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
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
 
@@ -3513,6 +3542,10 @@ var QueryProcessor = class {
3513
3542
  }
3514
3543
  /**
3515
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.
3516
3549
  */
3517
3550
  static buildQueryFilter(question, hints) {
3518
3551
  const filter = { metadata: {}, keywords: [], queryText: question };
@@ -3520,13 +3553,11 @@ var QueryProcessor = class {
3520
3553
  if (hint.field) {
3521
3554
  filter.metadata[hint.field] = hint.value;
3522
3555
  } else {
3523
- filter.keywords.push(hint.value);
3556
+ if (!filter.keywords.includes(hint.value)) {
3557
+ filter.keywords.push(hint.value);
3558
+ }
3524
3559
  }
3525
3560
  }
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
3561
  if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
3531
3562
  if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
3532
3563
  return filter;
@@ -3534,10 +3565,40 @@ var QueryProcessor = class {
3534
3565
  };
3535
3566
 
3536
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
+ };
3537
3597
  var Pipeline = class {
3538
3598
  constructor(config) {
3539
3599
  this.config = config;
3540
- this.embeddingCache = /* @__PURE__ */ new Map();
3600
+ /** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
3601
+ this.embeddingCache = new LRUEmbeddingCache(500);
3541
3602
  this.initialised = false;
3542
3603
  var _a, _b, _c, _d, _e;
3543
3604
  this.chunker = new DocumentChunker(
@@ -3625,6 +3686,7 @@ var Pipeline = class {
3625
3686
  }
3626
3687
  /**
3627
3688
  * Step 2: Generate embeddings for chunks with retry logic.
3689
+ * Uses batchEmbed when available for efficiency; falls back to sequential embedding.
3628
3690
  */
3629
3691
  async processEmbeddings(chunks) {
3630
3692
  const embedBatchOptions = {
@@ -3638,7 +3700,9 @@ var Pipeline = class {
3638
3700
  embedBatchOptions
3639
3701
  );
3640
3702
  if (vectors.length !== chunks.length) {
3641
- 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
+ );
3642
3706
  }
3643
3707
  return vectors;
3644
3708
  }
@@ -3792,6 +3856,7 @@ ${context}`;
3792
3856
  }
3793
3857
  /**
3794
3858
  * Universal retrieval method combining all enabled providers.
3859
+ * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
3795
3860
  */
3796
3861
  async retrieve(query, options) {
3797
3862
  var _a, _b, _c;
@@ -3824,10 +3889,13 @@ ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
3824
3889
  New Question: ${question}
3825
3890
 
3826
3891
  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
- ], "");
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
+ );
3831
3899
  return rewrite.trim() || question;
3832
3900
  }
3833
3901
  };
@@ -3988,21 +4056,11 @@ var VectorPlugin = class {
3988
4056
 
3989
4057
  // src/config/ConfigBuilder.ts
3990
4058
  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
4059
  /**
4002
4060
  * Set the project/application ID for namespacing
4003
4061
  */
4004
4062
  projectId(id) {
4005
- this.config.projectId = id;
4063
+ this._projectId = id;
4006
4064
  return this;
4007
4065
  }
4008
4066
  /**
@@ -4011,9 +4069,9 @@ var ConfigBuilder = class {
4011
4069
  vectorDb(provider, options) {
4012
4070
  var _a;
4013
4071
  if (provider === "auto") {
4014
- this.config.vectorDb = this.autoDetectVectorDb();
4072
+ this._vectorDb = this._autoDetectVectorDb();
4015
4073
  } else {
4016
- this.config.vectorDb = {
4074
+ this._vectorDb = {
4017
4075
  provider,
4018
4076
  indexName: (_a = options == null ? void 0 : options.indexName) != null ? _a : "default",
4019
4077
  options: __spreadValues({}, options)
@@ -4027,9 +4085,9 @@ var ConfigBuilder = class {
4027
4085
  llm(provider, model, apiKey, options) {
4028
4086
  var _a, _b;
4029
4087
  if (provider === "auto") {
4030
- this.config.llm = this.autoDetectLLM();
4088
+ this._llm = this._autoDetectLLM();
4031
4089
  } else {
4032
- this.config.llm = {
4090
+ this._llm = {
4033
4091
  provider,
4034
4092
  model: model != null ? model : "default-model",
4035
4093
  apiKey,
@@ -4047,9 +4105,9 @@ var ConfigBuilder = class {
4047
4105
  */
4048
4106
  embedding(provider, model, apiKey, options) {
4049
4107
  if (provider === "auto") {
4050
- this.config.embedding = this.autoDetectEmbedding();
4108
+ this._embedding = this._autoDetectEmbedding();
4051
4109
  } else {
4052
- this.config.embedding = {
4110
+ this._embedding = {
4053
4111
  provider,
4054
4112
  model: model != null ? model : "default-embedding",
4055
4113
  apiKey,
@@ -4060,26 +4118,46 @@ var ConfigBuilder = class {
4060
4118
  return this;
4061
4119
  }
4062
4120
  /**
4063
- * Set RAG-specific parameters
4121
+ * Set RAG-specific pipeline parameters
4064
4122
  */
4065
4123
  rag(options) {
4066
4124
  var _a;
4067
- this.config.rag = __spreadValues(__spreadValues({}, (_a = this.config.rag) != null ? _a : {}), options);
4125
+ this._rag = __spreadValues(__spreadValues({}, (_a = this._rag) != null ? _a : {}), options);
4068
4126
  return this;
4069
4127
  }
4070
4128
  /**
4071
- * Set UI/UX parameters
4129
+ * Set UI branding and appearance options.
4130
+ * Accepts the full UIConfig interface.
4072
4131
  */
4073
4132
  ui(options) {
4074
- this.config.ui = options;
4133
+ this._ui = options;
4075
4134
  return this;
4076
4135
  }
4077
4136
  /**
4078
- * Build and return the configuration
4137
+ * Build and return the final RagConfig.
4138
+ * Throws if required fields (projectId, vectorDb, llm, embedding) are not set.
4079
4139
  */
4080
4140
  build() {
4081
- const finalConfig = this.config;
4082
- 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 } : {});
4083
4161
  }
4084
4162
  /**
4085
4163
  * Build and return as JSON for serialization
@@ -4090,169 +4168,58 @@ var ConfigBuilder = class {
4090
4168
  // ============================================================================
4091
4169
  // Private helper methods for auto-detection
4092
4170
  // ============================================================================
4093
- autoDetectVectorDb() {
4171
+ _autoDetectVectorDb() {
4094
4172
  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
- };
4173
+ return { provider: "pinecone", indexName: process.env.PINECONE_INDEX, options: { apiKey: process.env.PINECONE_API_KEY } };
4100
4174
  }
4101
4175
  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
- };
4176
+ return { provider: "qdrant", indexName: process.env.QDRANT_COLLECTION || "documents", options: { url: process.env.QDRANT_URL, apiKey: process.env.QDRANT_API_KEY } };
4110
4177
  }
4111
4178
  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
- };
4179
+ return { provider: "postgresql", indexName: process.env.PG_TABLE || "documents", options: { connectionString: process.env.DATABASE_URL } };
4119
4180
  }
4120
4181
  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
- };
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" } };
4130
4183
  }
4131
4184
  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
- };
4185
+ return { provider: "redis", indexName: process.env.REDIS_INDEX || "documents", options: { url: process.env.REDIS_URL } };
4139
4186
  }
4140
- return {
4141
- provider: "qdrant",
4142
- indexName: "documents",
4143
- options: {
4144
- url: process.env.QDRANT_URL || "http://localhost:6333"
4145
- }
4146
- };
4187
+ return { provider: "qdrant", indexName: "documents", options: { url: process.env.QDRANT_URL || "http://localhost:6333" } };
4147
4188
  }
4148
- autoDetectLLM() {
4189
+ _autoDetectLLM() {
4149
4190
  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
- };
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") };
4157
4192
  }
4158
4193
  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
- };
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") };
4166
4195
  }
4167
4196
  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
- };
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") };
4175
4198
  }
4176
- return {
4177
- provider: "openai",
4178
- model: "gpt-4o-mini",
4179
- apiKey: process.env.OPENAI_API_KEY
4180
- };
4199
+ return { provider: "openai", model: "gpt-4o-mini", apiKey: process.env.OPENAI_API_KEY };
4181
4200
  }
4182
- autoDetectEmbedding() {
4201
+ _autoDetectEmbedding() {
4183
4202
  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
- };
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 };
4191
4204
  }
4192
- return {
4193
- provider: "openai",
4194
- model: "text-embedding-3-small",
4195
- apiKey: process.env.OPENAI_API_KEY
4196
- };
4205
+ return { provider: "openai", model: "text-embedding-3-small", apiKey: process.env.OPENAI_API_KEY };
4197
4206
  }
4198
4207
  };
4199
4208
  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
- }
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" }
4256
4223
  };
4257
4224
  function createFromPreset(presetName) {
4258
4225
  const preset = PRESETS[presetName];
@@ -4531,8 +4498,35 @@ init_UniversalVectorProvider();
4531
4498
 
4532
4499
  // src/handlers/index.ts
4533
4500
  var import_server = require("next/server");
4534
- function createChatHandler(config) {
4535
- 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);
4536
4530
  return async function POST(req) {
4537
4531
  try {
4538
4532
  const body = await req.json();
@@ -4548,8 +4542,64 @@ function createChatHandler(config) {
4548
4542
  }
4549
4543
  };
4550
4544
  }
4551
- function createIngestHandler(config) {
4552
- 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);
4553
4603
  return async function POST(req) {
4554
4604
  try {
4555
4605
  const body = await req.json();
@@ -4565,8 +4615,8 @@ function createIngestHandler(config) {
4565
4615
  }
4566
4616
  };
4567
4617
  }
4568
- function createHealthHandler(config) {
4569
- const plugin = new VectorPlugin(config);
4618
+ function createHealthHandler(configOrPlugin) {
4619
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4570
4620
  return async function GET() {
4571
4621
  try {
4572
4622
  const health = await plugin.checkHealth();
@@ -4582,8 +4632,8 @@ function createHealthHandler(config) {
4582
4632
  }
4583
4633
  };
4584
4634
  }
4585
- function createUploadHandler(config) {
4586
- const plugin = new VectorPlugin(config);
4635
+ function createUploadHandler(configOrPlugin) {
4636
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4587
4637
  return async function POST(req) {
4588
4638
  try {
4589
4639
  const formData = await req.formData();
@@ -4652,6 +4702,12 @@ function createUploadHandler(config) {
4652
4702
  createFromPreset,
4653
4703
  createHealthHandler,
4654
4704
  createIngestHandler,
4705
+ createStreamHandler,
4655
4706
  createUploadHandler,
4656
- getRagConfig
4707
+ getRagConfig,
4708
+ resetConfigCache,
4709
+ sseErrorFrame,
4710
+ sseFrame,
4711
+ sseMetaFrame,
4712
+ sseTextFrame
4657
4713
  });