@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
@@ -1508,7 +1508,11 @@ __export(handlers_exports, {
1508
1508
  createHealthHandler: () => createHealthHandler,
1509
1509
  createIngestHandler: () => createIngestHandler,
1510
1510
  createStreamHandler: () => createStreamHandler,
1511
- createUploadHandler: () => createUploadHandler
1511
+ createUploadHandler: () => createUploadHandler,
1512
+ sseErrorFrame: () => sseErrorFrame,
1513
+ sseFrame: () => sseFrame,
1514
+ sseMetaFrame: () => sseMetaFrame,
1515
+ sseTextFrame: () => sseTextFrame
1512
1516
  });
1513
1517
  module.exports = __toCommonJS(handlers_exports);
1514
1518
  var import_server = require("next/server");
@@ -1691,13 +1695,20 @@ function getRagConfig(env = process.env) {
1691
1695
  }
1692
1696
 
1693
1697
  // src/core/ConfigResolver.ts
1698
+ var _cachedEnvConfig = null;
1699
+ function getCachedEnvConfig() {
1700
+ if (!_cachedEnvConfig) {
1701
+ _cachedEnvConfig = getRagConfig();
1702
+ }
1703
+ return _cachedEnvConfig;
1704
+ }
1694
1705
  var ConfigResolver = class {
1695
1706
  /**
1696
- * Resolves the final configuration by merging host-provided config with defaults.
1707
+ * Resolves the final configuration by merging host-provided config with environment defaults.
1697
1708
  * @param hostConfig - Partial configuration passed from the host application.
1698
1709
  */
1699
1710
  static resolve(hostConfig) {
1700
- const envConfig = getRagConfig();
1711
+ const envConfig = getCachedEnvConfig();
1701
1712
  if (!hostConfig) {
1702
1713
  return envConfig;
1703
1714
  }
@@ -2971,14 +2982,16 @@ var LangChainAgent = class {
2971
2982
  this.config = config;
2972
2983
  }
2973
2984
  /**
2974
- * Initializes the agent with the RAG pipeline as a primary tool.
2975
- * Dynamically imports LangChain dependencies to avoid build errors if missing.
2985
+ * Initializes the LangChain ReAct agent with the RAG pipeline as a tool.
2986
+ * Dynamically imports langchain so the package doesn't crash if it isn't installed.
2976
2987
  */
2977
2988
  async initialize(chatModel) {
2978
2989
  try {
2979
- const { DynamicTool } = await import(`${"@langchain/core/tools"}`);
2980
- const { ChatPromptTemplate, MessagesPlaceholder } = await import(`${"@langchain/core/prompts"}`);
2981
- const { AgentExecutor, createOpenAIFunctionsAgent } = await import(`${"langchain/agents"}`);
2990
+ const [{ DynamicTool }, { HumanMessage }, { createAgent }] = await Promise.all([
2991
+ import("@langchain/core/tools"),
2992
+ import("@langchain/core/messages"),
2993
+ import("langchain")
2994
+ ]);
2982
2995
  const searchTool = new DynamicTool({
2983
2996
  name: "document_search",
2984
2997
  description: "Use this tool to search through the knowledge base and document repository. Input should be a specific search query.",
@@ -2987,42 +3000,53 @@ var LangChainAgent = class {
2987
3000
  return `Search Results:
2988
3001
  ${response.reply}
2989
3002
 
2990
- Sources Used: ${JSON.stringify(response.sources.map((s) => s.id))}`;
3003
+ Sources Used: ${JSON.stringify(
3004
+ response.sources.map((s) => s.id)
3005
+ )}`;
2991
3006
  }
2992
3007
  });
2993
- const tools = [searchTool];
2994
- const prompt = ChatPromptTemplate.fromMessages([
2995
- ["system", this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."],
2996
- new MessagesPlaceholder("chat_history"),
2997
- ["human", "{input}"],
2998
- new MessagesPlaceholder("agent_scratchpad")
2999
- ]);
3000
- const agent = await createOpenAIFunctionsAgent({
3001
- llm: chatModel,
3002
- tools,
3003
- prompt
3004
- });
3005
- this.executor = new AgentExecutor({
3006
- agent,
3007
- tools
3008
+ this.agent = createAgent({
3009
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3010
+ model: chatModel,
3011
+ tools: [searchTool],
3012
+ systemPrompt: this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."
3008
3013
  });
3014
+ void HumanMessage;
3009
3015
  } catch (error) {
3010
- console.error("[LangChainAgent] Failed to initialize. Ensure 'langchain' and '@langchain/core' are installed.");
3011
- throw error;
3016
+ const isMissing = error instanceof Error && error.message.includes("Cannot find module");
3017
+ const hint = isMissing ? " Make sure 'langchain' and '@langchain/core' are installed:\n npm install langchain @langchain/core" : "";
3018
+ throw new Error(
3019
+ `[LangChainAgent] Failed to initialize.${hint}
3020
+ ${error instanceof Error ? error.message : String(error)}`
3021
+ );
3012
3022
  }
3013
3023
  }
3014
3024
  /**
3015
3025
  * Run the agentic flow.
3026
+ *
3027
+ * LangChain v1.x: invoke takes `{ messages: [...] }` instead of `{ input, chat_history }`.
3028
+ * The agent returns `{ messages: [...] }` — the last message is the final answer.
3016
3029
  */
3017
3030
  async run(input, chatHistory = []) {
3018
- if (!this.executor) {
3031
+ var _a, _b, _c;
3032
+ if (!this.agent) {
3019
3033
  throw new Error("[LangChainAgent] Agent not initialized. Call initialize() first.");
3020
3034
  }
3021
- const response = await this.executor.invoke({
3022
- input,
3023
- chat_history: chatHistory
3035
+ const { HumanMessage, AIMessage } = await import("@langchain/core/messages");
3036
+ const historyMessages = chatHistory.map(
3037
+ (m) => m.role === "user" ? new HumanMessage(m.content) : new AIMessage(m.content)
3038
+ );
3039
+ const response = await this.agent.invoke({
3040
+ messages: [...historyMessages, new HumanMessage(input)]
3024
3041
  });
3025
- return response.output;
3042
+ const lastMessage = (_a = response == null ? void 0 : response.messages) == null ? void 0 : _a.at(-1);
3043
+ if (lastMessage && typeof lastMessage.content === "string") {
3044
+ return lastMessage.content;
3045
+ }
3046
+ if (Array.isArray(lastMessage == null ? void 0 : lastMessage.content)) {
3047
+ return lastMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("");
3048
+ }
3049
+ return String((_c = (_b = response == null ? void 0 : response.output) != null ? _b : response) != null ? _c : "");
3026
3050
  }
3027
3051
  };
3028
3052
 
@@ -3338,15 +3362,18 @@ var QueryProcessor = class {
3338
3362
  * Checks if a string is likely a question word or common prompt phrase.
3339
3363
  */
3340
3364
  static isLikelyPromptPhrase(value) {
3341
- return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
3365
+ 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());
3342
3366
  }
3343
3367
  /**
3344
3368
  * Scans a natural language question for potential metadata hints and keywords.
3369
+ * @param question The user's query
3370
+ * @param validFields Optional list of known filterable fields to look for
3345
3371
  */
3346
- static extractQueryFieldHints(question) {
3372
+ static extractQueryFieldHints(question, validFields = []) {
3347
3373
  var _a, _b, _c, _d;
3348
3374
  if (!question.trim()) return [];
3349
3375
  const hints = /* @__PURE__ */ new Map();
3376
+ const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
3350
3377
  const addHint = (value, field) => {
3351
3378
  const normalizedValue = this.normalizeHintValue(value);
3352
3379
  if (!normalizedValue) return;
@@ -3377,11 +3404,28 @@ var QueryProcessor = class {
3377
3404
  if (name) addHint(name, "name");
3378
3405
  }
3379
3406
  }
3407
+ if (fieldsToSearch.length > 0) {
3408
+ for (const field of fieldsToSearch) {
3409
+ const forwardPattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{2,60})["']?(?=[?.!,]|$)`, "gi");
3410
+ for (const match of question.matchAll(forwardPattern)) {
3411
+ const value = match[1];
3412
+ if (value && !this.isLikelyPromptPhrase(value)) {
3413
+ addHint(value, field);
3414
+ }
3415
+ }
3416
+ const reversePattern = new RegExp(`\\b([^"\\n?.!,\\s]{2,60})\\s+${field}\\b`, "gi");
3417
+ for (const match of question.matchAll(reversePattern)) {
3418
+ const value = match[1];
3419
+ if (value && !this.isLikelyPromptPhrase(value)) {
3420
+ addHint(value, field);
3421
+ }
3422
+ }
3423
+ }
3424
+ }
3380
3425
  const universalPatterns = [
3381
3426
  { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: "email", group: 1 },
3382
3427
  { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: "phone", group: 1 },
3383
3428
  { 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 },
3384
- { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: "amount", group: 1 },
3385
3429
  { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: "id", group: 2 },
3386
3430
  { regex: /"([^"]{2,120})"/g, group: 1 },
3387
3431
  { regex: /'([^']{2,120})'/g, group: 1 }
@@ -3425,6 +3469,10 @@ var QueryProcessor = class {
3425
3469
  }
3426
3470
  /**
3427
3471
  * Constructs a QueryFilter object from extracted hints.
3472
+ *
3473
+ * Note: proper nouns are extracted inside `extractQueryFieldHints` and surfaced
3474
+ * as field-less hints; `buildQueryFilter` adds them to `keywords` without
3475
+ * re-running the proper noun regex to avoid duplication.
3428
3476
  */
3429
3477
  static buildQueryFilter(question, hints) {
3430
3478
  const filter = { metadata: {}, keywords: [], queryText: question };
@@ -3432,24 +3480,74 @@ var QueryProcessor = class {
3432
3480
  if (hint.field) {
3433
3481
  filter.metadata[hint.field] = hint.value;
3434
3482
  } else {
3435
- filter.keywords.push(hint.value);
3483
+ if (!filter.keywords.includes(hint.value)) {
3484
+ filter.keywords.push(hint.value);
3485
+ }
3436
3486
  }
3437
3487
  }
3438
- for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
3439
- const term = this.normalizeHintValue(match[0]);
3440
- if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
3441
- }
3442
3488
  if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
3443
3489
  if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
3444
3490
  return filter;
3445
3491
  }
3446
3492
  };
3493
+ QueryProcessor.COMMON_METADATA_FIELDS = [
3494
+ "category",
3495
+ "type",
3496
+ "brand",
3497
+ "status",
3498
+ "priority",
3499
+ "id",
3500
+ "name",
3501
+ "email",
3502
+ "phone",
3503
+ "price",
3504
+ "rating",
3505
+ "color",
3506
+ "size",
3507
+ "material",
3508
+ "sku",
3509
+ "role",
3510
+ "department",
3511
+ "location",
3512
+ "tag",
3513
+ "label"
3514
+ ];
3447
3515
 
3448
3516
  // src/core/Pipeline.ts
3517
+ var LRUEmbeddingCache = class {
3518
+ constructor(maxSize = 500) {
3519
+ this.cache = /* @__PURE__ */ new Map();
3520
+ this.maxSize = maxSize;
3521
+ }
3522
+ get(key) {
3523
+ const value = this.cache.get(key);
3524
+ if (value !== void 0) {
3525
+ this.cache.delete(key);
3526
+ this.cache.set(key, value);
3527
+ }
3528
+ return value;
3529
+ }
3530
+ set(key, value) {
3531
+ if (this.cache.has(key)) {
3532
+ this.cache.delete(key);
3533
+ } else if (this.cache.size >= this.maxSize) {
3534
+ const oldestKey = this.cache.keys().next().value;
3535
+ if (oldestKey !== void 0) this.cache.delete(oldestKey);
3536
+ }
3537
+ this.cache.set(key, value);
3538
+ }
3539
+ clear() {
3540
+ this.cache.clear();
3541
+ }
3542
+ get size() {
3543
+ return this.cache.size;
3544
+ }
3545
+ };
3449
3546
  var Pipeline = class {
3450
3547
  constructor(config) {
3451
3548
  this.config = config;
3452
- this.embeddingCache = /* @__PURE__ */ new Map();
3549
+ /** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
3550
+ this.embeddingCache = new LRUEmbeddingCache(500);
3453
3551
  this.initialised = false;
3454
3552
  var _a, _b, _c, _d, _e;
3455
3553
  this.chunker = new DocumentChunker(
@@ -3537,6 +3635,7 @@ var Pipeline = class {
3537
3635
  }
3538
3636
  /**
3539
3637
  * Step 2: Generate embeddings for chunks with retry logic.
3638
+ * Uses batchEmbed when available for efficiency; falls back to sequential embedding.
3540
3639
  */
3541
3640
  async processEmbeddings(chunks) {
3542
3641
  const embedBatchOptions = {
@@ -3550,7 +3649,9 @@ var Pipeline = class {
3550
3649
  embedBatchOptions
3551
3650
  );
3552
3651
  if (vectors.length !== chunks.length) {
3553
- throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
3652
+ throw new Error(
3653
+ `[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
3654
+ );
3554
3655
  }
3555
3656
  return vectors;
3556
3657
  }
@@ -3640,7 +3741,7 @@ var Pipeline = class {
3640
3741
  */
3641
3742
  askStream(_0) {
3642
3743
  return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
3643
- var _a, _b, _c, _d, _e, _f;
3744
+ var _a, _b, _c, _d, _e, _f, _g;
3644
3745
  yield new __await(this.initialize());
3645
3746
  const ns = namespace != null ? namespace : this.config.projectId;
3646
3747
  const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
@@ -3650,7 +3751,7 @@ var Pipeline = class {
3650
3751
  if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
3651
3752
  searchQuery = yield new __await(this.rewriteQuery(question, history));
3652
3753
  }
3653
- const hints = QueryProcessor.extractQueryFieldHints(question);
3754
+ const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
3654
3755
  const filter = QueryProcessor.buildQueryFilter(question, hints);
3655
3756
  const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
3656
3757
  namespace: ns,
@@ -3658,7 +3759,7 @@ var Pipeline = class {
3658
3759
  filter
3659
3760
  }));
3660
3761
  let sources = rawSources.filter((m) => m.score >= scoreThreshold);
3661
- if ((_f = this.config.rag) == null ? void 0 : _f.useReranking) {
3762
+ if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
3662
3763
  sources = yield new __await(this.reranker.rerank(sources, question, topK));
3663
3764
  } else {
3664
3765
  sources = sources.slice(0, topK);
@@ -3704,6 +3805,7 @@ ${context}`;
3704
3805
  }
3705
3806
  /**
3706
3807
  * Universal retrieval method combining all enabled providers.
3808
+ * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
3707
3809
  */
3708
3810
  async retrieve(query, options) {
3709
3811
  var _a, _b, _c;
@@ -3736,10 +3838,13 @@ ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
3736
3838
  New Question: ${question}
3737
3839
 
3738
3840
  Optimized Search Query:`;
3739
- const rewrite = await this.llmProvider.chat([
3740
- { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
3741
- { role: "user", content: prompt }
3742
- ], "");
3841
+ const rewrite = await this.llmProvider.chat(
3842
+ [
3843
+ { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
3844
+ { role: "user", content: prompt }
3845
+ ],
3846
+ ""
3847
+ );
3743
3848
  return rewrite.trim() || question;
3744
3849
  }
3745
3850
  };
@@ -3958,8 +4063,35 @@ var DocumentParser = class {
3958
4063
  };
3959
4064
 
3960
4065
  // src/handlers/index.ts
3961
- function createChatHandler(config) {
3962
- const plugin = new VectorPlugin(config);
4066
+ function sseFrame(payload) {
4067
+ return `data: ${JSON.stringify(payload)}
4068
+
4069
+ `;
4070
+ }
4071
+ function sseTextFrame(text) {
4072
+ return `data: ${JSON.stringify({ type: "text", text })}
4073
+
4074
+ `;
4075
+ }
4076
+ function sseMetaFrame(meta) {
4077
+ return `data: ${JSON.stringify(__spreadValues({ type: "metadata" }, meta != null ? meta : {}))}
4078
+
4079
+ `;
4080
+ }
4081
+ function sseErrorFrame(message) {
4082
+ return `data: ${JSON.stringify({ type: "error", error: message })}
4083
+
4084
+ `;
4085
+ }
4086
+ var SSE_HEADERS = {
4087
+ "Content-Type": "text/event-stream; charset=utf-8",
4088
+ "Cache-Control": "no-cache, no-transform",
4089
+ Connection: "keep-alive",
4090
+ "X-Accel-Buffering": "no"
4091
+ // Disable Nginx buffering for streaming
4092
+ };
4093
+ function createChatHandler(configOrPlugin) {
4094
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
3963
4095
  return async function POST(req) {
3964
4096
  try {
3965
4097
  const body = await req.json();
@@ -3975,67 +4107,64 @@ function createChatHandler(config) {
3975
4107
  }
3976
4108
  };
3977
4109
  }
3978
- function createStreamHandler(config) {
3979
- const plugin = new VectorPlugin(config);
4110
+ function createStreamHandler(configOrPlugin) {
4111
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
3980
4112
  return async function POST(req) {
4113
+ let body;
3981
4114
  try {
3982
- const body = await req.json();
3983
- const { message, history = [], namespace } = body;
3984
- const encoder = new TextEncoder();
3985
- const stream = new ReadableStream({
3986
- async start(controller) {
4115
+ body = await req.json();
4116
+ } catch (e) {
4117
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
4118
+ status: 400,
4119
+ headers: { "Content-Type": "application/json" }
4120
+ });
4121
+ }
4122
+ const { message, history = [], namespace } = body;
4123
+ if (!(message == null ? void 0 : message.trim())) {
4124
+ return new Response(JSON.stringify({ error: "message is required" }), {
4125
+ status: 400,
4126
+ headers: { "Content-Type": "application/json" }
4127
+ });
4128
+ }
4129
+ const encoder = new TextEncoder();
4130
+ const stream = new ReadableStream({
4131
+ async start(controller) {
4132
+ const enqueue = (text) => controller.enqueue(encoder.encode(text));
4133
+ try {
4134
+ const pipelineStream = plugin.chatStream(message, history, namespace);
3987
4135
  try {
3988
- const pipelineStream = plugin.chatStream(message, history, namespace);
3989
- try {
3990
- for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
3991
- const chunk = temp.value;
3992
- if (typeof chunk === "string") {
3993
- controller.enqueue(encoder.encode(chunk));
3994
- } else {
3995
- controller.enqueue(encoder.encode(`
3996
-
3997
- __METADATA__${JSON.stringify(chunk)}`));
3998
- }
4136
+ for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
4137
+ const chunk = temp.value;
4138
+ if (typeof chunk === "string") {
4139
+ enqueue(sseTextFrame(chunk));
4140
+ } else {
4141
+ enqueue(sseMetaFrame(chunk));
3999
4142
  }
4000
- } catch (temp) {
4001
- error = [temp];
4143
+ }
4144
+ } catch (temp) {
4145
+ error = [temp];
4146
+ } finally {
4147
+ try {
4148
+ more && (temp = iter.return) && await temp.call(iter);
4002
4149
  } finally {
4003
- try {
4004
- more && (temp = iter.return) && await temp.call(iter);
4005
- } finally {
4006
- if (error)
4007
- throw error[0];
4008
- }
4150
+ if (error)
4151
+ throw error[0];
4009
4152
  }
4010
- controller.close();
4011
- } catch (streamError) {
4012
- console.error("[createStreamHandler] Stream processing error:", streamError);
4013
- const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
4014
- controller.enqueue(encoder.encode(`
4015
-
4016
- __ERROR__${JSON.stringify({ error: errorMessage })}`));
4017
- controller.close();
4018
4153
  }
4154
+ } catch (streamError) {
4155
+ const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
4156
+ console.error("[createStreamHandler] Stream error:", streamError);
4157
+ enqueue(sseErrorFrame(errorMessage));
4158
+ } finally {
4159
+ controller.close();
4019
4160
  }
4020
- });
4021
- return new Response(stream, {
4022
- headers: {
4023
- "Content-Type": "text/event-stream",
4024
- "Cache-Control": "no-cache",
4025
- "Connection": "keep-alive"
4026
- }
4027
- });
4028
- } catch (err) {
4029
- const message = err instanceof Error ? err.message : "Internal server error";
4030
- return new Response(JSON.stringify({ error: message }), {
4031
- status: 500,
4032
- headers: { "Content-Type": "application/json" }
4033
- });
4034
- }
4161
+ }
4162
+ });
4163
+ return new Response(stream, { headers: SSE_HEADERS });
4035
4164
  };
4036
4165
  }
4037
- function createIngestHandler(config) {
4038
- const plugin = new VectorPlugin(config);
4166
+ function createIngestHandler(configOrPlugin) {
4167
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4039
4168
  return async function POST(req) {
4040
4169
  try {
4041
4170
  const body = await req.json();
@@ -4051,8 +4180,8 @@ function createIngestHandler(config) {
4051
4180
  }
4052
4181
  };
4053
4182
  }
4054
- function createHealthHandler(config) {
4055
- const plugin = new VectorPlugin(config);
4183
+ function createHealthHandler(configOrPlugin) {
4184
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4056
4185
  return async function GET() {
4057
4186
  try {
4058
4187
  const health = await plugin.checkHealth();
@@ -4068,8 +4197,8 @@ function createHealthHandler(config) {
4068
4197
  }
4069
4198
  };
4070
4199
  }
4071
- function createUploadHandler(config) {
4072
- const plugin = new VectorPlugin(config);
4200
+ function createUploadHandler(configOrPlugin) {
4201
+ const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
4073
4202
  return async function POST(req) {
4074
4203
  try {
4075
4204
  const formData = await req.formData();
@@ -4107,5 +4236,9 @@ function createUploadHandler(config) {
4107
4236
  createHealthHandler,
4108
4237
  createIngestHandler,
4109
4238
  createStreamHandler,
4110
- createUploadHandler
4239
+ createUploadHandler,
4240
+ sseErrorFrame,
4241
+ sseFrame,
4242
+ sseMetaFrame,
4243
+ sseTextFrame
4111
4244
  });
@@ -3,8 +3,12 @@ import {
3
3
  createHealthHandler,
4
4
  createIngestHandler,
5
5
  createStreamHandler,
6
- createUploadHandler
7
- } from "../chunk-6MLZHQZT.mjs";
6
+ createUploadHandler,
7
+ sseErrorFrame,
8
+ sseFrame,
9
+ sseMetaFrame,
10
+ sseTextFrame
11
+ } from "../chunk-ZCDJSGUW.mjs";
8
12
  import "../chunk-YLTMFW4M.mjs";
9
13
  import "../chunk-X4TOT24V.mjs";
10
14
  export {
@@ -12,5 +16,9 @@ export {
12
16
  createHealthHandler,
13
17
  createIngestHandler,
14
18
  createStreamHandler,
15
- createUploadHandler
19
+ createUploadHandler,
20
+ sseErrorFrame,
21
+ sseFrame,
22
+ sseMetaFrame,
23
+ sseTextFrame
16
24
  };