@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.
- package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
- package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
- package/dist/{RagConfig-DRJO4hGU.d.mts → RagConfig-BEmz4hVP.d.mts} +58 -1
- package/dist/{RagConfig-DRJO4hGU.d.ts → RagConfig-BEmz4hVP.d.ts} +58 -1
- package/dist/{chunk-6MLZHQZT.mjs → chunk-MWL4AGQI.mjs} +189 -98
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +191 -100
- package/dist/handlers/index.mjs +11 -3
- package/dist/index-CSfaCeux.d.ts +206 -0
- package/dist/index-DFSy0CG6.d.mts +206 -0
- package/dist/index.d.mts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +103 -89
- package/dist/index.mjs +91 -95
- package/dist/server.d.mts +45 -97
- package/dist/server.d.ts +45 -97
- package/dist/server.js +271 -215
- package/dist/server.mjs +78 -167
- package/package.json +12 -10
- package/src/components/ChatWindow.tsx +2 -2
- package/src/components/ConfigProvider.tsx +2 -2
- package/src/components/MessageBubble.tsx +2 -2
- package/src/components/SourceCard.tsx +1 -1
- package/src/components/ThemeToggle.tsx +1 -1
- package/src/config/ConfigBuilder.ts +86 -211
- package/src/config/uiConstants.ts +23 -0
- package/src/core/ConfigResolver.ts +57 -29
- package/src/core/LangChainAgent.ts +73 -40
- package/src/core/Pipeline.ts +63 -7
- package/src/core/ProviderRegistry.ts +2 -2
- package/src/core/QueryProcessor.ts +9 -8
- package/src/handlers/index.ts +138 -49
- package/src/hooks/useRagChat.ts +71 -32
- package/src/server.ts +12 -2
- package/dist/DocumentChunker-C-sCZPhi.d.mts +0 -102
- package/dist/DocumentChunker-C-sCZPhi.d.ts +0 -102
- package/dist/index-B2mutkgp.d.ts +0 -116
- package/dist/index-Bjy0es5a.d.mts +0 -116
package/dist/handlers/index.js
CHANGED
|
@@ -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 =
|
|
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
|
|
2975
|
-
* Dynamically imports
|
|
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
|
|
2980
|
-
|
|
2981
|
-
|
|
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(
|
|
3003
|
+
Sources Used: ${JSON.stringify(
|
|
3004
|
+
response.sources.map((s) => s.id)
|
|
3005
|
+
)}`;
|
|
2991
3006
|
}
|
|
2992
3007
|
});
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
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
|
-
|
|
3011
|
-
|
|
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
|
-
|
|
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
|
|
3022
|
-
|
|
3023
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -3425,6 +3449,10 @@ var QueryProcessor = class {
|
|
|
3425
3449
|
}
|
|
3426
3450
|
/**
|
|
3427
3451
|
* Constructs a QueryFilter object from extracted hints.
|
|
3452
|
+
*
|
|
3453
|
+
* Note: proper nouns are extracted inside `extractQueryFieldHints` and surfaced
|
|
3454
|
+
* as field-less hints; `buildQueryFilter` adds them to `keywords` without
|
|
3455
|
+
* re-running the proper noun regex to avoid duplication.
|
|
3428
3456
|
*/
|
|
3429
3457
|
static buildQueryFilter(question, hints) {
|
|
3430
3458
|
const filter = { metadata: {}, keywords: [], queryText: question };
|
|
@@ -3432,13 +3460,11 @@ var QueryProcessor = class {
|
|
|
3432
3460
|
if (hint.field) {
|
|
3433
3461
|
filter.metadata[hint.field] = hint.value;
|
|
3434
3462
|
} else {
|
|
3435
|
-
filter.keywords.
|
|
3463
|
+
if (!filter.keywords.includes(hint.value)) {
|
|
3464
|
+
filter.keywords.push(hint.value);
|
|
3465
|
+
}
|
|
3436
3466
|
}
|
|
3437
3467
|
}
|
|
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
3468
|
if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
|
|
3443
3469
|
if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
|
|
3444
3470
|
return filter;
|
|
@@ -3446,10 +3472,40 @@ var QueryProcessor = class {
|
|
|
3446
3472
|
};
|
|
3447
3473
|
|
|
3448
3474
|
// src/core/Pipeline.ts
|
|
3475
|
+
var LRUEmbeddingCache = class {
|
|
3476
|
+
constructor(maxSize = 500) {
|
|
3477
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
3478
|
+
this.maxSize = maxSize;
|
|
3479
|
+
}
|
|
3480
|
+
get(key) {
|
|
3481
|
+
const value = this.cache.get(key);
|
|
3482
|
+
if (value !== void 0) {
|
|
3483
|
+
this.cache.delete(key);
|
|
3484
|
+
this.cache.set(key, value);
|
|
3485
|
+
}
|
|
3486
|
+
return value;
|
|
3487
|
+
}
|
|
3488
|
+
set(key, value) {
|
|
3489
|
+
if (this.cache.has(key)) {
|
|
3490
|
+
this.cache.delete(key);
|
|
3491
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
3492
|
+
const oldestKey = this.cache.keys().next().value;
|
|
3493
|
+
if (oldestKey !== void 0) this.cache.delete(oldestKey);
|
|
3494
|
+
}
|
|
3495
|
+
this.cache.set(key, value);
|
|
3496
|
+
}
|
|
3497
|
+
clear() {
|
|
3498
|
+
this.cache.clear();
|
|
3499
|
+
}
|
|
3500
|
+
get size() {
|
|
3501
|
+
return this.cache.size;
|
|
3502
|
+
}
|
|
3503
|
+
};
|
|
3449
3504
|
var Pipeline = class {
|
|
3450
3505
|
constructor(config) {
|
|
3451
3506
|
this.config = config;
|
|
3452
|
-
|
|
3507
|
+
/** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
|
|
3508
|
+
this.embeddingCache = new LRUEmbeddingCache(500);
|
|
3453
3509
|
this.initialised = false;
|
|
3454
3510
|
var _a, _b, _c, _d, _e;
|
|
3455
3511
|
this.chunker = new DocumentChunker(
|
|
@@ -3537,6 +3593,7 @@ var Pipeline = class {
|
|
|
3537
3593
|
}
|
|
3538
3594
|
/**
|
|
3539
3595
|
* Step 2: Generate embeddings for chunks with retry logic.
|
|
3596
|
+
* Uses batchEmbed when available for efficiency; falls back to sequential embedding.
|
|
3540
3597
|
*/
|
|
3541
3598
|
async processEmbeddings(chunks) {
|
|
3542
3599
|
const embedBatchOptions = {
|
|
@@ -3550,7 +3607,9 @@ var Pipeline = class {
|
|
|
3550
3607
|
embedBatchOptions
|
|
3551
3608
|
);
|
|
3552
3609
|
if (vectors.length !== chunks.length) {
|
|
3553
|
-
throw new Error(
|
|
3610
|
+
throw new Error(
|
|
3611
|
+
`[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
|
|
3612
|
+
);
|
|
3554
3613
|
}
|
|
3555
3614
|
return vectors;
|
|
3556
3615
|
}
|
|
@@ -3704,6 +3763,7 @@ ${context}`;
|
|
|
3704
3763
|
}
|
|
3705
3764
|
/**
|
|
3706
3765
|
* Universal retrieval method combining all enabled providers.
|
|
3766
|
+
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
3707
3767
|
*/
|
|
3708
3768
|
async retrieve(query, options) {
|
|
3709
3769
|
var _a, _b, _c;
|
|
@@ -3736,10 +3796,13 @@ ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
|
|
|
3736
3796
|
New Question: ${question}
|
|
3737
3797
|
|
|
3738
3798
|
Optimized Search Query:`;
|
|
3739
|
-
const rewrite = await this.llmProvider.chat(
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3799
|
+
const rewrite = await this.llmProvider.chat(
|
|
3800
|
+
[
|
|
3801
|
+
{ role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
|
|
3802
|
+
{ role: "user", content: prompt }
|
|
3803
|
+
],
|
|
3804
|
+
""
|
|
3805
|
+
);
|
|
3743
3806
|
return rewrite.trim() || question;
|
|
3744
3807
|
}
|
|
3745
3808
|
};
|
|
@@ -3958,8 +4021,35 @@ var DocumentParser = class {
|
|
|
3958
4021
|
};
|
|
3959
4022
|
|
|
3960
4023
|
// src/handlers/index.ts
|
|
3961
|
-
function
|
|
3962
|
-
|
|
4024
|
+
function sseFrame(payload) {
|
|
4025
|
+
return `data: ${JSON.stringify(payload)}
|
|
4026
|
+
|
|
4027
|
+
`;
|
|
4028
|
+
}
|
|
4029
|
+
function sseTextFrame(text) {
|
|
4030
|
+
return `data: ${JSON.stringify({ type: "text", text })}
|
|
4031
|
+
|
|
4032
|
+
`;
|
|
4033
|
+
}
|
|
4034
|
+
function sseMetaFrame(meta) {
|
|
4035
|
+
return `data: ${JSON.stringify(__spreadValues({ type: "metadata" }, meta != null ? meta : {}))}
|
|
4036
|
+
|
|
4037
|
+
`;
|
|
4038
|
+
}
|
|
4039
|
+
function sseErrorFrame(message) {
|
|
4040
|
+
return `data: ${JSON.stringify({ type: "error", error: message })}
|
|
4041
|
+
|
|
4042
|
+
`;
|
|
4043
|
+
}
|
|
4044
|
+
var SSE_HEADERS = {
|
|
4045
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
4046
|
+
"Cache-Control": "no-cache, no-transform",
|
|
4047
|
+
Connection: "keep-alive",
|
|
4048
|
+
"X-Accel-Buffering": "no"
|
|
4049
|
+
// Disable Nginx buffering for streaming
|
|
4050
|
+
};
|
|
4051
|
+
function createChatHandler(configOrPlugin) {
|
|
4052
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
3963
4053
|
return async function POST(req) {
|
|
3964
4054
|
try {
|
|
3965
4055
|
const body = await req.json();
|
|
@@ -3975,67 +4065,64 @@ function createChatHandler(config) {
|
|
|
3975
4065
|
}
|
|
3976
4066
|
};
|
|
3977
4067
|
}
|
|
3978
|
-
function createStreamHandler(
|
|
3979
|
-
const plugin = new VectorPlugin(
|
|
4068
|
+
function createStreamHandler(configOrPlugin) {
|
|
4069
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
3980
4070
|
return async function POST(req) {
|
|
4071
|
+
let body;
|
|
3981
4072
|
try {
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
4073
|
+
body = await req.json();
|
|
4074
|
+
} catch (e) {
|
|
4075
|
+
return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
|
|
4076
|
+
status: 400,
|
|
4077
|
+
headers: { "Content-Type": "application/json" }
|
|
4078
|
+
});
|
|
4079
|
+
}
|
|
4080
|
+
const { message, history = [], namespace } = body;
|
|
4081
|
+
if (!(message == null ? void 0 : message.trim())) {
|
|
4082
|
+
return new Response(JSON.stringify({ error: "message is required" }), {
|
|
4083
|
+
status: 400,
|
|
4084
|
+
headers: { "Content-Type": "application/json" }
|
|
4085
|
+
});
|
|
4086
|
+
}
|
|
4087
|
+
const encoder = new TextEncoder();
|
|
4088
|
+
const stream = new ReadableStream({
|
|
4089
|
+
async start(controller) {
|
|
4090
|
+
const enqueue = (text) => controller.enqueue(encoder.encode(text));
|
|
4091
|
+
try {
|
|
4092
|
+
const pipelineStream = plugin.chatStream(message, history, namespace);
|
|
3987
4093
|
try {
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
} else {
|
|
3995
|
-
controller.enqueue(encoder.encode(`
|
|
3996
|
-
|
|
3997
|
-
__METADATA__${JSON.stringify(chunk)}`));
|
|
3998
|
-
}
|
|
4094
|
+
for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
4095
|
+
const chunk = temp.value;
|
|
4096
|
+
if (typeof chunk === "string") {
|
|
4097
|
+
enqueue(sseTextFrame(chunk));
|
|
4098
|
+
} else {
|
|
4099
|
+
enqueue(sseMetaFrame(chunk));
|
|
3999
4100
|
}
|
|
4000
|
-
}
|
|
4001
|
-
|
|
4101
|
+
}
|
|
4102
|
+
} catch (temp) {
|
|
4103
|
+
error = [temp];
|
|
4104
|
+
} finally {
|
|
4105
|
+
try {
|
|
4106
|
+
more && (temp = iter.return) && await temp.call(iter);
|
|
4002
4107
|
} finally {
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
} finally {
|
|
4006
|
-
if (error)
|
|
4007
|
-
throw error[0];
|
|
4008
|
-
}
|
|
4108
|
+
if (error)
|
|
4109
|
+
throw error[0];
|
|
4009
4110
|
}
|
|
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
4111
|
}
|
|
4112
|
+
} catch (streamError) {
|
|
4113
|
+
const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
|
|
4114
|
+
console.error("[createStreamHandler] Stream error:", streamError);
|
|
4115
|
+
enqueue(sseErrorFrame(errorMessage));
|
|
4116
|
+
} finally {
|
|
4117
|
+
controller.close();
|
|
4019
4118
|
}
|
|
4020
|
-
}
|
|
4021
|
-
|
|
4022
|
-
|
|
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
|
-
}
|
|
4119
|
+
}
|
|
4120
|
+
});
|
|
4121
|
+
return new Response(stream, { headers: SSE_HEADERS });
|
|
4035
4122
|
};
|
|
4036
4123
|
}
|
|
4037
|
-
function createIngestHandler(
|
|
4038
|
-
const plugin = new VectorPlugin(
|
|
4124
|
+
function createIngestHandler(configOrPlugin) {
|
|
4125
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
4039
4126
|
return async function POST(req) {
|
|
4040
4127
|
try {
|
|
4041
4128
|
const body = await req.json();
|
|
@@ -4051,8 +4138,8 @@ function createIngestHandler(config) {
|
|
|
4051
4138
|
}
|
|
4052
4139
|
};
|
|
4053
4140
|
}
|
|
4054
|
-
function createHealthHandler(
|
|
4055
|
-
const plugin = new VectorPlugin(
|
|
4141
|
+
function createHealthHandler(configOrPlugin) {
|
|
4142
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
4056
4143
|
return async function GET() {
|
|
4057
4144
|
try {
|
|
4058
4145
|
const health = await plugin.checkHealth();
|
|
@@ -4068,8 +4155,8 @@ function createHealthHandler(config) {
|
|
|
4068
4155
|
}
|
|
4069
4156
|
};
|
|
4070
4157
|
}
|
|
4071
|
-
function createUploadHandler(
|
|
4072
|
-
const plugin = new VectorPlugin(
|
|
4158
|
+
function createUploadHandler(configOrPlugin) {
|
|
4159
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
4073
4160
|
return async function POST(req) {
|
|
4074
4161
|
try {
|
|
4075
4162
|
const formData = await req.formData();
|
|
@@ -4107,5 +4194,9 @@ function createUploadHandler(config) {
|
|
|
4107
4194
|
createHealthHandler,
|
|
4108
4195
|
createIngestHandler,
|
|
4109
4196
|
createStreamHandler,
|
|
4110
|
-
createUploadHandler
|
|
4197
|
+
createUploadHandler,
|
|
4198
|
+
sseErrorFrame,
|
|
4199
|
+
sseFrame,
|
|
4200
|
+
sseMetaFrame,
|
|
4201
|
+
sseTextFrame
|
|
4111
4202
|
});
|
package/dist/handlers/index.mjs
CHANGED
|
@@ -3,8 +3,12 @@ import {
|
|
|
3
3
|
createHealthHandler,
|
|
4
4
|
createIngestHandler,
|
|
5
5
|
createStreamHandler,
|
|
6
|
-
createUploadHandler
|
|
7
|
-
|
|
6
|
+
createUploadHandler,
|
|
7
|
+
sseErrorFrame,
|
|
8
|
+
sseFrame,
|
|
9
|
+
sseMetaFrame,
|
|
10
|
+
sseTextFrame
|
|
11
|
+
} from "../chunk-MWL4AGQI.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
|
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { g as RagConfig, C as ChatMessage, b as ChatResponse, e as IngestDocument } from './RagConfig-BEmz4hVP.js';
|
|
2
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
3
|
+
|
|
4
|
+
interface ValidationError {
|
|
5
|
+
field: string;
|
|
6
|
+
message: string;
|
|
7
|
+
suggestion?: string;
|
|
8
|
+
severity: 'error' | 'warning';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* ConfigValidator.ts — Comprehensive configuration validation.
|
|
12
|
+
* Uses a pluggable architecture to delegate provider-specific checks.
|
|
13
|
+
*/
|
|
14
|
+
declare class ConfigValidator {
|
|
15
|
+
/**
|
|
16
|
+
* Validates the entire RagConfig object.
|
|
17
|
+
*/
|
|
18
|
+
static validate(config: RagConfig): Promise<ValidationError[]>;
|
|
19
|
+
private static validateVectorDbConfig;
|
|
20
|
+
private static validateLLMConfig;
|
|
21
|
+
private static validateEmbeddingConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Temporary fallbacks for providers not yet migrated to the pluggable architecture.
|
|
24
|
+
*/
|
|
25
|
+
private static fallbackVectorValidation;
|
|
26
|
+
private static fallbackLLMValidation;
|
|
27
|
+
private static fallbackEmbeddingValidation;
|
|
28
|
+
private static validateUIConfig;
|
|
29
|
+
private static validateRAGConfig;
|
|
30
|
+
private static isValidCSSColor;
|
|
31
|
+
static validateAndThrow(config: RagConfig): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Interface for provider-specific configuration validation logic.
|
|
36
|
+
*/
|
|
37
|
+
interface IProviderValidator {
|
|
38
|
+
/**
|
|
39
|
+
* Validates the configuration for a specific provider.
|
|
40
|
+
* @param config - The configuration object to validate.
|
|
41
|
+
* @returns An array of validation errors (empty if valid).
|
|
42
|
+
*/
|
|
43
|
+
validate(config: Record<string, unknown>): ValidationError[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Result of a provider health check.
|
|
47
|
+
*/
|
|
48
|
+
interface HealthCheckResult {
|
|
49
|
+
healthy: boolean;
|
|
50
|
+
provider: string;
|
|
51
|
+
version?: string;
|
|
52
|
+
capabilities?: Record<string, unknown>;
|
|
53
|
+
error?: string;
|
|
54
|
+
timestamp: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Interface for provider-specific health checking logic.
|
|
58
|
+
*/
|
|
59
|
+
interface IProviderHealthChecker {
|
|
60
|
+
/**
|
|
61
|
+
* Performs a health check for a specific provider.
|
|
62
|
+
* @param config - The configuration object used to connect to the provider.
|
|
63
|
+
* @returns A promise that resolves to the health check result.
|
|
64
|
+
*/
|
|
65
|
+
check(config: Record<string, unknown>): Promise<HealthCheckResult>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* VectorPlugin — main orchestrator class.
|
|
70
|
+
* This is the primary interface for host applications.
|
|
71
|
+
*
|
|
72
|
+
* Features:
|
|
73
|
+
* - Configuration resolution from host + environment
|
|
74
|
+
* - Configuration validation with detailed error messages
|
|
75
|
+
* - Provider health checks before initialization
|
|
76
|
+
* - Multi-provider support (Vector DBs & LLMs)
|
|
77
|
+
* - Per-tenant data isolation via namespacing
|
|
78
|
+
*/
|
|
79
|
+
declare class VectorPlugin {
|
|
80
|
+
private config;
|
|
81
|
+
private pipeline;
|
|
82
|
+
private validationPromise;
|
|
83
|
+
constructor(hostConfig?: Partial<RagConfig>);
|
|
84
|
+
/**
|
|
85
|
+
* Get the current resolved configuration.
|
|
86
|
+
*/
|
|
87
|
+
getConfig(): RagConfig;
|
|
88
|
+
/**
|
|
89
|
+
* Perform pre-flight health checks on all configured providers.
|
|
90
|
+
* Useful to verify connectivity before running operations.
|
|
91
|
+
*
|
|
92
|
+
* @returns Health status for vector DB, LLM, and embedding providers
|
|
93
|
+
*/
|
|
94
|
+
checkHealth(): Promise<{
|
|
95
|
+
vectorDb: HealthCheckResult;
|
|
96
|
+
llm: HealthCheckResult;
|
|
97
|
+
embedding?: HealthCheckResult;
|
|
98
|
+
allHealthy: boolean;
|
|
99
|
+
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Run a chat query.
|
|
102
|
+
*/
|
|
103
|
+
chat(message: string, history?: ChatMessage[], namespace?: string): Promise<ChatResponse>;
|
|
104
|
+
/**
|
|
105
|
+
* Run a streaming chat query.
|
|
106
|
+
*/
|
|
107
|
+
chatStream(message: string, history?: ChatMessage[], namespace?: string): AsyncGenerator<string | ChatResponse, void, any>;
|
|
108
|
+
/**
|
|
109
|
+
* Ingest documents into the vector database.
|
|
110
|
+
*/
|
|
111
|
+
ingest(documents: IngestDocument[], namespace?: string): Promise<Array<{
|
|
112
|
+
docId: string | number;
|
|
113
|
+
chunksIngested: number;
|
|
114
|
+
}>>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Encode a payload as a Server-Sent Events frame.
|
|
119
|
+
* data: <json>\n\n
|
|
120
|
+
*/
|
|
121
|
+
declare function sseFrame(payload: unknown): string;
|
|
122
|
+
/** Encode a plain text chunk as an SSE frame. */
|
|
123
|
+
declare function sseTextFrame(text: string): string;
|
|
124
|
+
/** Encode the retrieval metadata as an SSE frame. */
|
|
125
|
+
declare function sseMetaFrame(meta: unknown): string;
|
|
126
|
+
/** Encode a stream error as an SSE frame. */
|
|
127
|
+
declare function sseErrorFrame(message: string): string;
|
|
128
|
+
/**
|
|
129
|
+
* createChatHandler — factory that returns a Next.js App Router POST handler.
|
|
130
|
+
*
|
|
131
|
+
* Accepts either a full/partial `RagConfig` **or** a pre-built `VectorPlugin`
|
|
132
|
+
* instance (preferred for singleton/multi-tenant setups).
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Option A — pass config (plugin created once at module load time)
|
|
136
|
+
* export const POST = createChatHandler(getRagConfig());
|
|
137
|
+
*
|
|
138
|
+
* // Option B — pass a pre-built plugin (most flexible)
|
|
139
|
+
* const plugin = new VectorPlugin(getRagConfig());
|
|
140
|
+
* export const POST = createChatHandler(plugin);
|
|
141
|
+
*/
|
|
142
|
+
declare function createChatHandler(configOrPlugin?: Partial<RagConfig> | VectorPlugin): (req: NextRequest) => Promise<NextResponse<{
|
|
143
|
+
error: string;
|
|
144
|
+
}> | NextResponse<ChatResponse>>;
|
|
145
|
+
/**
|
|
146
|
+
* createStreamHandler — factory for a streaming SSE Next.js App Router POST handler.
|
|
147
|
+
*
|
|
148
|
+
* Streams the assistant response as Server-Sent Events (SSE). Each frame is a
|
|
149
|
+
* JSON object with a `type` discriminant:
|
|
150
|
+
* - `{ type: "text", text: "..." }` — incremental text chunk
|
|
151
|
+
* - `{ type: "metadata", sources: [...] }` — retrieval metadata (last frame)
|
|
152
|
+
* - `{ type: "error", error: "..." }` — stream-level error
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* // src/app/api/chat/route.ts
|
|
156
|
+
* export const POST = createStreamHandler(getRagConfig());
|
|
157
|
+
*
|
|
158
|
+
* // Client-side parsing in useRagChat.ts:
|
|
159
|
+
* // const lines = chunk.split('\n');
|
|
160
|
+
* // for (const line of lines) {
|
|
161
|
+
* // if (line.startsWith('data: ')) {
|
|
162
|
+
* // const frame = JSON.parse(line.slice(6));
|
|
163
|
+
* // if (frame.type === 'text') { ... }
|
|
164
|
+
* // }
|
|
165
|
+
* // }
|
|
166
|
+
*/
|
|
167
|
+
declare function createStreamHandler(configOrPlugin?: Partial<RagConfig> | VectorPlugin): (req: NextRequest) => Promise<Response>;
|
|
168
|
+
/**
|
|
169
|
+
* createIngestHandler — factory for the document ingestion endpoint.
|
|
170
|
+
*/
|
|
171
|
+
declare function createIngestHandler(configOrPlugin?: Partial<RagConfig> | VectorPlugin): (req: NextRequest) => Promise<NextResponse<{
|
|
172
|
+
error: string;
|
|
173
|
+
}> | NextResponse<{
|
|
174
|
+
results: {
|
|
175
|
+
docId: string | number;
|
|
176
|
+
chunksIngested: number;
|
|
177
|
+
}[];
|
|
178
|
+
}>>;
|
|
179
|
+
/**
|
|
180
|
+
* createHealthHandler — factory for the health-check endpoint.
|
|
181
|
+
*/
|
|
182
|
+
declare function createHealthHandler(configOrPlugin?: Partial<RagConfig> | VectorPlugin): () => Promise<NextResponse<{
|
|
183
|
+
timestamp: string;
|
|
184
|
+
vectorDb: HealthCheckResult;
|
|
185
|
+
llm: HealthCheckResult;
|
|
186
|
+
embedding?: HealthCheckResult;
|
|
187
|
+
allHealthy: boolean;
|
|
188
|
+
status: string;
|
|
189
|
+
}> | NextResponse<{
|
|
190
|
+
status: string;
|
|
191
|
+
error: string;
|
|
192
|
+
}>>;
|
|
193
|
+
/**
|
|
194
|
+
* createUploadHandler — factory for the file upload ingestion endpoint.
|
|
195
|
+
*/
|
|
196
|
+
declare function createUploadHandler(configOrPlugin?: Partial<RagConfig> | VectorPlugin): (req: NextRequest) => Promise<NextResponse<{
|
|
197
|
+
error: string;
|
|
198
|
+
}> | NextResponse<{
|
|
199
|
+
message: string;
|
|
200
|
+
results: {
|
|
201
|
+
docId: string | number;
|
|
202
|
+
chunksIngested: number;
|
|
203
|
+
}[];
|
|
204
|
+
}>>;
|
|
205
|
+
|
|
206
|
+
export { ConfigValidator as C, type HealthCheckResult as H, type IProviderValidator as I, type ValidationError as V, type IProviderHealthChecker as a, VectorPlugin as b, createChatHandler as c, createHealthHandler as d, createIngestHandler as e, createStreamHandler as f, createUploadHandler as g, sseFrame as h, sseMetaFrame as i, sseTextFrame as j, sseErrorFrame as s };
|